View Javadoc
1   package de.dlr.shepard.data.timeseries.utilities;
2   
3   import com.opencsv.bean.CsvToBeanBuilder;
4   import de.dlr.shepard.common.exceptions.InvalidBodyException;
5   import de.dlr.shepard.common.exceptions.InvalidRequestException;
6   import de.dlr.shepard.data.timeseries.io.TimeseriesWithDataPoints;
7   import de.dlr.shepard.data.timeseries.model.TimeseriesDataPoint;
8   import de.dlr.shepard.data.timeseries.model.TimeseriesTuple;
9   import de.dlr.shepard.data.timeseries.model.enums.CsvFormat;
10  import io.quarkus.logging.Log;
11  import java.io.IOException;
12  import java.io.InputStream;
13  import java.io.InputStreamReader;
14  import java.nio.charset.StandardCharsets;
15  import java.util.ArrayList;
16  import java.util.HashMap;
17  import java.util.List;
18  import java.util.Locale;
19  import java.util.stream.Collectors;
20  import org.apache.commons.lang3.math.NumberUtils;
21  
22  public final class CsvConverter {
23  
24    private CsvConverter() {}
25  
26    public static InputStream convertToCsv(
27      TimeseriesTuple timeseries,
28      List<TimeseriesDataPoint> dataPoints,
29      CsvFormat format
30    ) {
31      return convertToCsv(List.of(new TimeseriesWithDataPoints(timeseries, dataPoints)), format);
32    }
33  
34    public static InputStream convertToCsv(
35      List<TimeseriesWithDataPoints> timeseriesWithDataPointsList,
36      CsvFormat format
37    ) {
38      CsvFormat formatNonNull = format != null ? format : CsvFormat.ROW;
39      CsvLineProvider provider =
40        switch (formatNonNull) {
41          case ROW -> new CsvRowLineProvider(timeseriesWithDataPointsList);
42          case COLUMN -> new CsvColumnLineProvider(timeseriesWithDataPointsList);
43        };
44      return new CsvInputStream(provider);
45    }
46  
47    public static List<TimeseriesWithDataPoints> convertToTimeseriesWithData(InputStream stream) {
48      try (var reader = new InputStreamReader(stream)) {
49        var timeseriesDataBuilder = new CsvToBeanBuilder<CsvTimeseriesDataPoint>(reader)
50          .withType(CsvTimeseriesDataPoint.class)
51          .withErrorLocale(Locale.forLanguageTag("en"))
52          .withExceptionHandler(e -> {
53            var encoder = StandardCharsets.ISO_8859_1.newEncoder();
54            var message = encoder.canEncode(e.getMessage()) ? e.getMessage() : "Invalid CSV";
55            Log.errorf("CsvException while reading stream: %s", message);
56            throw new InvalidBodyException(message);
57          })
58          .build();
59  
60        List<CsvTimeseriesDataPoint> result = timeseriesDataBuilder.parse();
61  
62        return convertCsvToTimeseriesWithData(result);
63      } catch (IOException e) {
64        Log.error("IOException while reading the provided InputStream", e);
65        throw new InvalidRequestException();
66      }
67    }
68  
69    private static List<TimeseriesWithDataPoints> convertCsvToTimeseriesWithData(
70      List<CsvTimeseriesDataPoint> csvInputList
71    ) {
72      HashMap<TimeseriesTuple, List<TimeseriesDataPoint>> result = new HashMap<
73        TimeseriesTuple,
74        List<TimeseriesDataPoint>
75      >();
76  
77      for (var csvInputLine : csvInputList) {
78        var timeseries = new TimeseriesTuple(
79          csvInputLine.getMeasurement(),
80          csvInputLine.getDevice(),
81          csvInputLine.getLocation(),
82          csvInputLine.getSymbolicName(),
83          csvInputLine.getField()
84        );
85        var dataPoint = new TimeseriesDataPoint(csvInputLine.getTimestamp(), parseValue(csvInputLine.getValue()));
86  
87        if (result.containsKey(timeseries)) {
88          result.get(timeseries).add(dataPoint);
89        } else {
90          var dataPoints = new ArrayList<TimeseriesDataPoint>();
91          dataPoints.add(dataPoint);
92          result.put(timeseries, dataPoints);
93        }
94      }
95      List<TimeseriesWithDataPoints> timeseriesWithDataPointsList = result
96        .entrySet()
97        .stream()
98        .map(entry -> new TimeseriesWithDataPoints(entry.getKey(), entry.getValue()))
99        .collect(Collectors.toList());
100     return timeseriesWithDataPointsList;
101   }
102 
103   private static Object parseValue(Object input) {
104     List<String> boolString = List.of("true", "false");
105     if (input instanceof String sInput) {
106       if (NumberUtils.isCreatable(sInput)) {
107         try {
108           Integer intValue = Integer.parseInt(sInput);
109           return intValue;
110         } catch (NumberFormatException e) {
111           Double doubleValue = Double.parseDouble(sInput);
112           return doubleValue;
113         }
114       } else if (boolString.contains(sInput.toLowerCase())) {
115         return sInput.equalsIgnoreCase("true");
116       }
117     }
118     return input;
119   }
120 }