From 9b8743b5c7b7f1ae9f93b85bdbbc62a32cebfdb4 Mon Sep 17 00:00:00 2001 From: Eddie Groh <groh@fortiss.org> Date: Mon, 18 Sep 2023 19:16:31 +0200 Subject: [PATCH] Added better csv export, better data handling and update listener Issue-Ref: 4310 Issue-Url: https://git.fortiss.org/af3/af3/-/issues/4310 Signed-off-by: Eddie Groh <groh@fortiss.org> --- .../ext/quality/ui/view/fx/MetricsViewFx.fxml | 4 +- .../quality/ui/ModelQualityUIActivator.java | 4 + .../ui/view/fx/ModelQualityFXController.java | 55 +++++++++++- .../ext/quality/MetricUpdateListener.java | 33 +++++++ .../ext/quality/data/DataRootElement.java | 87 +++++++++++++++++++ .../ext/quality/data/MetricDataManager.java | 10 +-- .../quality/service/IModelQualityService.java | 10 +++ .../quality/service/ModelQualityService.java | 34 ++++++-- .../ext/quality/storage/CSVFileWriter.java | 26 +++--- 9 files changed, 235 insertions(+), 28 deletions(-) create mode 100644 org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/MetricUpdateListener.java create mode 100644 org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/DataRootElement.java diff --git a/org.fortiss.tooling.ext.quality.ui/res/org/fortiss/tooling/ext/quality/ui/view/fx/MetricsViewFx.fxml b/org.fortiss.tooling.ext.quality.ui/res/org/fortiss/tooling/ext/quality/ui/view/fx/MetricsViewFx.fxml index 68f373c7c..f9f1b276a 100644 --- a/org.fortiss.tooling.ext.quality.ui/res/org/fortiss/tooling/ext/quality/ui/view/fx/MetricsViewFx.fxml +++ b/org.fortiss.tooling.ext.quality.ui/res/org/fortiss/tooling/ext/quality/ui/view/fx/MetricsViewFx.fxml @@ -11,8 +11,9 @@ <?import javafx.scene.control.TabPane?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.BorderPane?> +<?import javafx.scene.text.Text?> -<SplitPane fx:id="metricsSplitPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" orientation="VERTICAL" prefHeight="400.0" prefWidth="1035.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1"> +<SplitPane fx:id="metricsSplitPane" dividerPositions="0.5" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" orientation="VERTICAL" prefHeight="400.0" prefWidth="1035.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1"> <items> <TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE"> <tabs> @@ -67,5 +68,6 @@ </Tab> </tabs> </TabPane> + <Text fx:id="bottomText" strokeType="OUTSIDE" strokeWidth="0.0" text="Save model to refresh" /> </items> </SplitPane> diff --git a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityUIActivator.java b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityUIActivator.java index 47787eb29..2f47db82b 100644 --- a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityUIActivator.java +++ b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/ModelQualityUIActivator.java @@ -19,6 +19,8 @@ import static org.eclipse.jface.resource.ResourceLocator.imageDescriptorFromBund import org.eclipse.core.runtime.Plugin; import org.eclipse.jface.resource.ImageDescriptor; +import org.fortiss.tooling.ext.quality.service.IModelQualityService; +import org.fortiss.tooling.ext.quality.ui.view.fx.ModelQualityFXViewPart; import org.osgi.framework.BundleContext; /** @@ -39,6 +41,8 @@ public class ModelQualityUIActivator extends Plugin { public void start(BundleContext context) throws Exception { super.start(context); plugin = this; + IModelQualityService.getInstance() + .registerMetricUpdateListener(ModelQualityFXViewPart.getMetricsFXController()); System.out.println("[Plugin] " + PLUGIN_ID + " started."); } diff --git a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/view/fx/ModelQualityFXController.java b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/view/fx/ModelQualityFXController.java index 4272d7043..501a2b369 100644 --- a/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/view/fx/ModelQualityFXController.java +++ b/org.fortiss.tooling.ext.quality.ui/src/org/fortiss/tooling/ext/quality/ui/view/fx/ModelQualityFXController.java @@ -28,6 +28,7 @@ import static org.fortiss.tooling.kernel.ui.util.SelectionUtils.checkAndPickFirs import static org.fortiss.tooling.kernel.utils.EcoreUtils.getFirstParentWithType; import java.text.DecimalFormat; +import java.time.format.DateTimeFormatter; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -40,6 +41,7 @@ import org.fortiss.tooling.common.ui.javafx.layout.CompositeFXControllerBase; import org.fortiss.tooling.common.ui.javafx.style.FillStyle; import org.fortiss.tooling.common.ui.javafx.style.FontStyle; import org.fortiss.tooling.common.ui.javafx.style.LineStyle; +import org.fortiss.tooling.ext.quality.MetricUpdateListener; import org.fortiss.tooling.ext.quality.data.MetricDataManager; import org.fortiss.tooling.ext.quality.data.MetricKey; import org.fortiss.tooling.ext.quality.data.MetricTreeNode; @@ -54,6 +56,7 @@ import org.fortiss.tooling.spiderchart.style.ChartStyle; import org.fortiss.tooling.spiderchart.style.DataSeriesStyle; import org.fortiss.tooling.spiderchart.style.LegendStyle; +import javafx.application.Platform; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.Node; @@ -64,6 +67,7 @@ import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; import javafx.scene.control.SplitPane; import javafx.scene.layout.BorderPane; +import javafx.scene.text.Text; /** * FX Controller for the Metrics view. @@ -73,7 +77,7 @@ import javafx.scene.layout.BorderPane; @SuppressWarnings("unchecked") public class ModelQualityFXController extends CompositeFXControllerBase<SplitPane, Node> - implements ISelectionListener { + implements ISelectionListener, MetricUpdateListener { /** ChoiceBox for selecting the metric which shall be displayed. */ @FXML @@ -91,6 +95,13 @@ public class ModelQualityFXController extends CompositeFXControllerBase<SplitPan @FXML private ScatterChart<Number, Number> scatterChart; + /** {@link Text} at the bottom of the view. */ + @FXML + private Text bottomText; + + /** Time format when displaying updating time. */ + private final DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm:ss"); + /** {@inheritDoc} */ @Override public String getFXMLLocation() { @@ -135,13 +146,17 @@ public class ModelQualityFXController extends CompositeFXControllerBase<SplitPan return; } - var root_provider = manager.getRootNodesMap().get(currentRootElement); + var rootDataElement = manager.getDataRootElementMap().get(currentRootElement); - if(root_provider == null) { - updateCharts("Currently no metrics available, try generating them"); + if(rootDataElement == null) { + bottomText.setText( + "Save model or select \"Export Metrics\" in project options to generate metrics"); + updateCharts("No metrics available"); return; } + bottomText.setText("Metrics last updated: " + + timeFormat.format(rootDataElement.getLastRefresh()) + ". Save model to refresh"); lastSelectedElement = selected; updateCharts("Selection does not have metric information"); } @@ -184,6 +199,12 @@ public class ModelQualityFXController extends CompositeFXControllerBase<SplitPan } } + if(choiceBox.getValue() == null && !choices.isEmpty()) { + // Set choice box to first element in choices, if there are any choices and none + // is currently selected + choiceBox.setValue(choices.get(0)); + } + if(!node.getChildren().isEmpty()) { if(choiceBox.getValue() != null) { @@ -233,6 +254,9 @@ public class ModelQualityFXController extends CompositeFXControllerBase<SplitPan } else { message = "Selection does not have metric information"; } + } else { + // Nothing selected, so remove the bottom text + bottomText.setText(""); } borderPane.setCenter(new Label(message)); pieChart.getData().clear(); @@ -300,4 +324,27 @@ public class ModelQualityFXController extends CompositeFXControllerBase<SplitPan SpiderChartViewer viewer = new SpiderChartViewer(spiderChart, chartStyle); return viewer; } + + /** {@inheritDoc} */ + @Override + public void metricUpdate(IProjectRootElement updatedElement) { + Platform.runLater(() -> { + if(lastSelectedElement == null) { + // If nothing is selected, there is nothing to update + return; + } + + var rootDataElement = ModelQualityService.getInstance().getMetricDataManagerInstance() + .getDataRootElementMap().get(updatedElement); + if(rootDataElement != null) { + bottomText.setText("Metrics last updated: " + + timeFormat.format(rootDataElement.getLastRefresh()) + + ". Save model to refresh"); + } else { + bottomText.setText( + "Save model or select \"Export Metrics\" in project options to generate metrics"); + } + updateCharts("Selection does not have metric information"); + }); + } } diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/MetricUpdateListener.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/MetricUpdateListener.java new file mode 100644 index 000000000..f72bf91fe --- /dev/null +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/MetricUpdateListener.java @@ -0,0 +1,33 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2023 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.tooling.ext.quality; + +import org.fortiss.tooling.kernel.model.IProjectRootElement; + +/** + * Listener getting called when metrics update + * + * @author groh + */ +public interface MetricUpdateListener { + /** + * Callback for metric update. This method will get called every time the metrics are updated. + * + * @param updatedElement + * the corresponding root element which was updated + */ + public void metricUpdate(IProjectRootElement updatedElement); +} diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/DataRootElement.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/DataRootElement.java new file mode 100644 index 000000000..d54ab629f --- /dev/null +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/DataRootElement.java @@ -0,0 +1,87 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2023 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.tooling.ext.quality.data; + +import java.time.LocalDateTime; + +import org.fortiss.tooling.kernel.extension.data.ITopLevelElement; + +/** + * Storage element corresponding to a {@Link IProjectRootElement}. It stores the last refresh time + * as {@link LocalDateTime}, its respective {@link ITopLevelElement} and the {@link MetricTreeNode} + * storing the data + * + * @author groh + */ +public class DataRootElement { + + /** the last time a metric tree was refreshed */ + private LocalDateTime lastRefresh; + + /** the last time the metric tree was refreshed */ + private ITopLevelElement topLevelElement; + + /** Root {@link MetricTreeNode} for the metric tree containg all data */ + private MetricTreeNode rootNode; + + /** + * Constructor: Creates a new instance of the {@link DataRootElement}. + * + * @param lastRefresh + * the last time the rootNode was refreshed + * @param topLevelElement + * the {@link ITopLevelElement} of the project + * @param rootNode + * the root node of the metric data tree + */ + public DataRootElement(LocalDateTime lastRefresh, ITopLevelElement topLevelElement, + MetricTreeNode rootNode) { + super(); + this.lastRefresh = lastRefresh; + this.topLevelElement = topLevelElement; + this.rootNode = rootNode; + } + + /** Returns lastRefresh. */ + public LocalDateTime getLastRefresh() { + return lastRefresh; + } + + /** Sets lastRefresh. */ + public void setLastRefresh(LocalDateTime lastRefresh) { + this.lastRefresh = lastRefresh; + } + + /** Returns topLevelElement. */ + public ITopLevelElement getTopLevelElement() { + return topLevelElement; + } + + /** Sets topLevelElement. */ + public void setTopLevelElement(ITopLevelElement topLevelElement) { + this.topLevelElement = topLevelElement; + } + + /** Returns rootNode. */ + public MetricTreeNode getRootNode() { + return rootNode; + } + + /** Sets rootNode. */ + public void setRootNode(MetricTreeNode metricTreeNode) { + this.rootNode = metricTreeNode; + } +} diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricDataManager.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricDataManager.java index 36dc2d044..f9e2d04ed 100644 --- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricDataManager.java +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/data/MetricDataManager.java @@ -32,13 +32,13 @@ public class MetricDataManager { private Map<EObject, MetricTreeNode> treeNodeLookupTable; /** a map to lookup the corresponding root tree node for a project */ - private Map<IProjectRootElement, MetricTreeNode> root_nodes; + private Map<IProjectRootElement, DataRootElement> dataRootElementMap; /** * Constructor: Creates a new instance of the MetricDataManager. */ public MetricDataManager() { - root_nodes = new HashMap<>(); + dataRootElementMap = new HashMap<>(); treeNodeLookupTable = new HashMap<>(); } @@ -51,9 +51,9 @@ public class MetricDataManager { /** * @return the map between {@link IProjectRootElement} and the corresponding root - * {@link MetricTreeNode}. + * {@link DataRootElement}. */ - public Map<IProjectRootElement, MetricTreeNode> getRootNodesMap() { - return root_nodes; + public Map<IProjectRootElement, DataRootElement> getDataRootElementMap() { + return dataRootElementMap; } } diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityService.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityService.java index 5168db337..f653e228c 100644 --- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityService.java +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/IModelQualityService.java @@ -17,6 +17,7 @@ package org.fortiss.tooling.ext.quality.service; import org.eclipse.emf.ecore.EObject; import org.fortiss.tooling.ext.quality.IMetricProvider; +import org.fortiss.tooling.ext.quality.MetricUpdateListener; import org.fortiss.tooling.kernel.extension.data.ITopLevelElement; /** @@ -36,4 +37,13 @@ public interface IModelQualityService { /** Schedules an analysis of the metrics and processes them. */ void scheduleMetricCollection(ITopLevelElement top); + + /** + * Registers the provided {@link MetricUpdateListener} to be called when the metrics are + * updated. + * + * @param listener + * to be registered + */ + void registerMetricUpdateListener(MetricUpdateListener listener); } diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/ModelQualityService.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/ModelQualityService.java index 75d1301f6..d02483b84 100644 --- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/ModelQualityService.java +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/service/ModelQualityService.java @@ -17,9 +17,12 @@ package org.fortiss.tooling.ext.quality.service; import static java.util.Collections.emptyList; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Queue; @@ -35,6 +38,8 @@ import org.eclipse.emf.ecore.EObject; import org.fortiss.tooling.base.model.element.IHierarchicElement; import org.fortiss.tooling.ext.quality.GraphMetricsProvider; import org.fortiss.tooling.ext.quality.IMetricProvider; +import org.fortiss.tooling.ext.quality.MetricUpdateListener; +import org.fortiss.tooling.ext.quality.data.DataRootElement; import org.fortiss.tooling.ext.quality.data.MetricDataManager; import org.fortiss.tooling.ext.quality.data.MetricKey; import org.fortiss.tooling.ext.quality.data.MetricTreeNode; @@ -84,6 +89,9 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider /** Instance of the MetricDataManager. */ private MetricDataManager metricDataManagerInstance = new MetricDataManager(); + /** List of {@link MetricUpdateListener} which are called when the metrics are updated */ + private List<MetricUpdateListener> metricUpdateListeners = new ArrayList<>(); + /** Top-level elements queued to be processed by the metric collector. */ private final Queue<ITopLevelElement> queuedProcessableElements = new ConcurrentLinkedQueue<ITopLevelElement>(); @@ -116,6 +124,12 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider metricCollectorJob.schedule(); } + /** {@inheritDoc} */ + @Override + public void registerMetricUpdateListener(MetricUpdateListener listener) { + this.metricUpdateListeners.add(listener); + } + /** {@inheritDoc} */ @Override public void startService() { @@ -146,14 +160,14 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider * @param topLvl * the {@link ITopLevelElement} on which the analysis should be performed */ - private void performMetricCollection(ITopLevelElement topLvl) { + private void performMetricCollection(ITopLevelElement topLevelElement) { // Sort root elements, as some metrics are saved into trees from other root elements // To avoid these problems, the elements have to be processed in a certain order // Currently the only element affected by this is the allocation table, which is // why push it to the end of the list List<IProjectRootElement> rootElements = new ArrayList<>(); - rootElements.addAll(EcoreUtils.getChildrenWithType(topLvl.getRootModelElement(), + rootElements.addAll(EcoreUtils.getChildrenWithType(topLevelElement.getRootModelElement(), IProjectRootElement.class)); String allocationTableClassName = @@ -169,10 +183,9 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider } return 0; }); - + Map<IProjectRootElement, DataRootElement> updatedElements = new HashMap<>(); for(IProjectRootElement rootElement : rootElements) { - var root_nodes = metricDataManagerInstance.getRootNodesMap(); MetricTreeNode rootNode = new MetricTreeNode(); if(rootElement instanceof IHierarchicElement) { @@ -207,12 +220,16 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider .filter(v -> v.getSeverity() == ESeverity.WARNING).count()); } } - - root_nodes.put(rootElement, rootNode); + DataRootElement dataRootElement = + new DataRootElement(LocalDateTime.now(), topLevelElement, rootNode); + metricDataManagerInstance.getDataRootElementMap().put(rootElement, dataRootElement); + updatedElements.put(rootElement, dataRootElement); metricDataManagerInstance.getTreeNodeLookupTable().put(rootElement, rootNode); + metricUpdateListeners.forEach(l -> l.metricUpdate(rootElement)); } + // Store all currently saved metrics to the csv - CSVFileWriter.metricExtractionToCSV(metricDataManagerInstance.getRootNodesMap()); + CSVFileWriter.metricExtractionToCSV(updatedElements); } /** @@ -363,7 +380,8 @@ public class ModelQualityService extends EObjectAwareServiceBase<IMetricProvider /** {@inheritDoc} */ @Override public void topLevelElementLoaded(ITopLevelElement element) { - // Nothing done + // Schedule metric collection when loading projects + scheduleMetricCollection(element); } /** {@inheritDoc} */ diff --git a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/storage/CSVFileWriter.java b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/storage/CSVFileWriter.java index 3798d6cfd..45559ca85 100644 --- a/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/storage/CSVFileWriter.java +++ b/org.fortiss.tooling.ext.quality/src/org/fortiss/tooling/ext/quality/storage/CSVFileWriter.java @@ -32,8 +32,8 @@ import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.fortiss.tooling.ext.quality.data.DataRootElement; import org.fortiss.tooling.ext.quality.data.MetricKey; -import org.fortiss.tooling.ext.quality.data.MetricTreeNode; import org.fortiss.tooling.kernel.model.IProjectRootElement; /** @@ -56,7 +56,7 @@ public class CSVFileWriter { * to convert into csv * */ - public static void metricExtractionToCSV(Map<IProjectRootElement, MetricTreeNode> map) { + public static void metricExtractionToCSV(Map<IProjectRootElement, DataRootElement> map) { // check if there is any data to export if(map == null || map.isEmpty()) { @@ -86,8 +86,8 @@ public class CSVFileWriter { } catch(IOException e) { e.printStackTrace(); } - writeMetricsToFile(map, allKeys, path, createNewIndex); } + writeMetricsToFile(map, allKeys, path, createNewIndex); } /** @@ -103,14 +103,15 @@ public class CSVFileWriter { * @param createNewIndex * if a index should be written before writing any values */ - private static void writeMetricsToFile(Map<IProjectRootElement, MetricTreeNode> map, + private static void writeMetricsToFile(Map<IProjectRootElement, DataRootElement> map, List<String> allKeys, Path path, boolean createNewIndex) { try(var writer = new BufferedWriter(new FileWriter(path.toFile(), true))) { if(createNewIndex) { // Create new index and write it into the first line of the file allKeys.add("timestamp"); - allKeys.add("root_name"); + allKeys.add("project_name"); + allKeys.add("root_element_name"); allKeys.add("name"); allKeys.add("children"); // Collect all MetricKeys, convert them to a string and append them to the allKeys @@ -123,15 +124,20 @@ public class CSVFileWriter { writer.write("\n"); } // Remove first 4 keys as they are not provided by the provider - List<String> valueKeys = allKeys.subList(4, allKeys.size()); + List<String> valueKeys = allKeys.subList(5, allKeys.size()); LocalDateTime dateTime = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; String formattedDateTime = dateTime.format(formatter); - map.forEach((rootProject, rootNode) -> { + map.forEach((rootElement, dataRootElement) -> { // iterate over all project roots - String rootName = rootProject.getName(); + String rootName = rootElement.getClass().getSimpleName(); + + var topLevelElement = dataRootElement.getTopLevelElement(); + String projectName = topLevelElement.getSaveableName(); + + var rootNode = dataRootElement.getRootNode(); rootNode.traverseTree(node -> { var integers = node.getStoredIntegers(); @@ -143,8 +149,8 @@ public class CSVFileWriter { .map(id -> id.toString()).reduce((a, b) -> a + " " + b).orElse(""); // These 4 data entries are just concatenated in front - var startStream = - Stream.of(formattedDateTime, rootName, node.getName(), children_ids); + var startStream = Stream.of(formattedDateTime, projectName, rootName, + node.getName(), children_ids); // Collect all values from provider in the correct order and combine var values = Stream.concat(startStream, valueKeys.stream() .map(String::toUpperCase).map(MetricKey::valueOf).map(k -> { -- GitLab