Skip to content

Commit 183c129

Browse files
Merge pull request #1113 from ie3-institute/ms/#1112-confusing-error-message-on-deprecated-input-data
Improving error message when using the outdated csv format
2 parents 4abf08b + 921a51e commit 183c129

File tree

14 files changed

+1248
-1209
lines changed

14 files changed

+1248
-1209
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Storage minimum level parameter removed from cylindrical thermal storage [#1123](https://github.com/ie3-institute/PowerSystemDataModel/issues/1123)
1717
- Converted eval-rst to myst syntax in ReadTheDocs, fixed line wrapping and widths[#1137](https://github.com/ie3-institute/PowerSystemDataModel/issues/1137)
1818
- Improving usage of streams on sql fetches [#827](https://github.com/ie3-institute/PowerSystemDataModel/issues/827)
19+
- Improving error message when using the outdated csv format [#1112](https://github.com/ie3-institute/PowerSystemDataModel/issues/1112)
20+
1921

2022
## [5.1.0] - 2024-06-24
2123

src/main/java/edu/ie3/datamodel/io/source/csv/CsvDataSource.java

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -161,41 +161,44 @@ protected Set<Path> getIndividualTimeSeriesFilePaths() {
161161
* occurred
162162
*/
163163
protected Map<String, String> buildFieldsToAttributes(
164-
final String csvRow, final String[] headline) {
164+
final String csvRow, final String[] headline) throws SourceException {
165165

166166
TreeMap<String, String> insensitiveFieldsToAttributes =
167167
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
168168

169-
try {
170-
String[] fieldVals = parseCsvRow(csvRow, csvSep);
171-
insensitiveFieldsToAttributes.putAll(
172-
IntStream.range(0, fieldVals.length)
173-
.boxed()
174-
.collect(
175-
Collectors.toMap(
176-
k -> StringUtils.snakeCaseToCamelCase(headline[k]), v -> fieldVals[v])));
169+
String[] fieldVals = parseCsvRow(csvRow, csvSep);
170+
insensitiveFieldsToAttributes.putAll(
171+
IntStream.range(0, Math.min(fieldVals.length, headline.length))
172+
.boxed()
173+
.collect(
174+
Collectors.toMap(
175+
k -> StringUtils.snakeCaseToCamelCase(headline[k]), v -> fieldVals[v])));
177176

178-
if (insensitiveFieldsToAttributes.size() != headline.length) {
179-
Set<String> fieldsToAttributesKeySet = insensitiveFieldsToAttributes.keySet();
180-
insensitiveFieldsToAttributes = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
181-
throw new SourceException(
182-
"The size of the headline does not fit to the size of the resulting fields to attributes mapping.\nHeadline: "
183-
+ String.join(", ", headline)
184-
+ "\nResultingMap: "
185-
+ String.join(", ", fieldsToAttributesKeySet)
186-
+ "\nCsvRow: "
187-
+ csvRow.trim()
188-
+ ".\nIs the csv separator in the file matching the separator provided in the constructor ('"
189-
+ csvSep
190-
+ "') and does the number of columns match the number of headline fields?");
191-
}
192-
} catch (Exception e) {
193-
log.error(
194-
"Cannot build fields to attributes map for row '{}' with headline '{}'.\nException: {}",
195-
csvRow.trim(),
196-
String.join(",", headline),
197-
e);
177+
if (fieldVals.length != headline.length) {
178+
throw new SourceException(
179+
"The size of the headline ("
180+
+ headline.length
181+
+ ") does not fit to the size of the attribute fields ("
182+
+ fieldVals.length
183+
+ ").\nHeadline: "
184+
+ String.join(", ", headline)
185+
+ "\nRow: "
186+
+ csvRow.trim()
187+
+ ".\nPlease check:"
188+
+ "\n - is the csv separator in the file matching the separator provided in the constructor ('"
189+
+ csvSep
190+
+ "')"
191+
+ "\n - does the number of columns match the number of headline fields "
192+
+ "\n - are you using a valid RFC 4180 formatted csv row?");
193+
}
194+
195+
if (insensitiveFieldsToAttributes.size() != fieldVals.length) {
196+
throw new SourceException(
197+
"There might be duplicate headline elements.\nHeadline: "
198+
+ String.join(", ", headline)
199+
+ ".\nPlease keep in mind that headlines are case-insensitive and underscores from snake case are ignored.");
198200
}
201+
199202
return insensitiveFieldsToAttributes;
200203
}
201204

@@ -252,7 +255,7 @@ Try<Stream<Map<String, String>>, SourceException> buildStreamWithFieldsToAttribu
252255
// is wanted to avoid a lock on the file), but this causes a closing of the stream as well.
253256
// As we still want to consume the data at other places, we start a new stream instead of
254257
// returning the original one
255-
return Success.of(csvRowFieldValueMapping(reader, headline).parallelStream());
258+
return csvRowFieldValueMapping(reader, headline);
256259
} catch (FileNotFoundException e) {
257260
if (allowFileNotExisting) {
258261
log.warn("Unable to find file '{}': {}", filePath, e.getMessage());
@@ -282,13 +285,20 @@ private Try<Path, SourceException> getFilePath(Class<? extends Entity> entityCla
282285
* @param headline of the file
283286
* @return a list of mapping
284287
*/
285-
protected List<Map<String, String>> csvRowFieldValueMapping(
288+
protected Try<Stream<Map<String, String>>, SourceException> csvRowFieldValueMapping(
286289
BufferedReader reader, String[] headline) {
287-
return reader
288-
.lines()
289-
.parallel()
290-
.map(csvRow -> buildFieldsToAttributes(csvRow, headline))
291-
.filter(map -> !map.isEmpty())
292-
.toList();
290+
return Try.scanStream(
291+
reader
292+
.lines()
293+
.parallel()
294+
.map(
295+
csvRow ->
296+
Try.of(
297+
() -> buildFieldsToAttributes(csvRow, headline),
298+
SourceException.class)),
299+
"Map<String, String>")
300+
.transform(
301+
stream -> stream.filter(map -> !map.isEmpty()),
302+
e -> new SourceException("Parsing csv row failed.", e));
293303
}
294304
}

src/main/java/edu/ie3/datamodel/io/source/csv/CsvIdCoordinateSource.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public List<CoordinateDistance> findCornerPoints(
179179
}
180180

181181
public int getCoordinateCount() {
182-
return idToCoordinate.keySet().size();
182+
return idToCoordinate.size();
183183
}
184184

185185
private Collection<Point> getCoordinatesInBoundingBox(
@@ -209,7 +209,7 @@ private Collection<Point> getCoordinatesInBoundingBox(
209209
// is wanted to avoid a lock on the file), but this causes a closing of the stream as well.
210210
// As we still want to consume the data at other places, we start a new stream instead of
211211
// returning the original one
212-
return Success.of(dataSource.csvRowFieldValueMapping(reader, headline).parallelStream());
212+
return dataSource.csvRowFieldValueMapping(reader, headline);
213213
} catch (IOException e) {
214214
return Failure.of(
215215
new SourceException("Cannot read the file for coordinate id to coordinate mapping.", e));

src/main/java/edu/ie3/datamodel/io/source/csv/CsvWeatherSource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ private Try<Stream<Map<String, String>>, SourceException> buildStreamWithFieldsT
240240
// is wanted to avoid a lock on the file), but this causes a closing of the stream as well.
241241
// As we still want to consume the data at other places, we start a new stream instead of
242242
// returning the original one
243-
return Success.of(dataSource.csvRowFieldValueMapping(reader, headline).parallelStream());
243+
return dataSource.csvRowFieldValueMapping(reader, headline);
244244
} catch (IOException e) {
245245
return Failure.of(
246246
new SourceException(

src/test/groovy/edu/ie3/datamodel/io/source/csv/CsvDataSourceTest.groovy

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package edu.ie3.datamodel.io.source.csv
77

8+
import edu.ie3.datamodel.exceptions.SourceException
89
import edu.ie3.datamodel.io.csv.CsvIndividualTimeSeriesMetaInformation
910
import edu.ie3.datamodel.io.naming.FileNamingStrategy
1011
import edu.ie3.datamodel.io.naming.timeseries.ColumnScheme
@@ -243,7 +244,6 @@ class CsvDataSourceTest extends Specification implements CsvTestDataMeta {
243244
]
244245
}
245246

246-
247247
def "A CsvDataSource should build a valid fields to attributes map with valid data and empty value fields as expected"() {
248248
given:
249249
def validHeadline = [
@@ -275,7 +275,7 @@ class CsvDataSourceTest extends Specification implements CsvTestDataMeta {
275275
]
276276
}
277277

278-
def "A CsvDataSource should be able to handle several errors when the csvRow is invalid or cannot be processed"() {
278+
def "A CsvDataSource should throw an exception if the headline and CSV row have different sizes"() {
279279
given:
280280
def validHeadline = [
281281
"uuid",
@@ -288,14 +288,42 @@ class CsvDataSourceTest extends Specification implements CsvTestDataMeta {
288288
"s_rated"
289289
] as String[]
290290

291-
expect:
292-
dummyCsvSource.buildFieldsToAttributes(invalidCsvRow, validHeadline) == [:]
291+
when:
292+
dummyCsvSource.buildFieldsToAttributes(invalidCsvRow, validHeadline)
293+
294+
then:
295+
def exception = thrown(SourceException)
296+
exception.getMessage().startsWith("The size of the headline (8) does not fit to the size of the attribute fields")
293297

294298
where:
295299
invalidCsvRow || explaination
296300
"5ebd8f7e-dedb-4017-bb86-6373c4b68eb8;25.0;100.0;0.95;98.0;test_bmTypeInput;50.0;25.0" || "wrong separator"
297-
"5ebd8f7e-dedb-4017-bb86-6373c4b68eb8,25.0,100.0,0.95,98.0,test_bmTypeInput" || "too less columns"
298-
"5ebd8f7e-dedb-4017-bb86-6373c4b68eb8,25.0,100.0,0.95,98.0,test_bmTypeInput,,,," || "too much columns"
301+
"5ebd8f7e-dedb-4017-bb86-6373c4b68eb8,25.0,100.0,0.95,98.0,test_bmTypeInput" || "too little columns"
302+
"5ebd8f7e-dedb-4017-bb86-6373c4b68eb8,25.0,100.0,0.95,98.0,test_bmTypeInput,,,," || "too many columns"
303+
}
304+
305+
306+
def "A CsvDataSource should throw an exception if there are duplicate headlines"() {
307+
given:
308+
def invalidHeadline = [
309+
"uuid",
310+
"active_power_gradient",
311+
"Active_Power_Gradient",
312+
"capex",
313+
"cosphi_rated",
314+
"eta_conv",
315+
"id",
316+
"opex",
317+
"s_rated",
318+
] as String[]
319+
def validCsvRow = "5ebd8f7e-dedb-4017-bb86-6373c4b68eb8,25.0,25.0,100.0,0.95,98.0,test_bmTypeInput,50.0,25.0"
320+
321+
when:
322+
dummyCsvSource.buildFieldsToAttributes(validCsvRow, invalidHeadline)
323+
324+
then:
325+
def exception = thrown(SourceException)
326+
exception.getMessage().startsWith("There might be duplicate headline elements.")
299327
}
300328

301329
def "The CsvDataSource is able to provide correct paths to time series files"() {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"uuid","cost_controlled","feed_in_tariff","id","market_reaction","node","operates_from","operates_until","operator","q_characteristics","type"
2-
a3b7576b-cac7-4350-90ff-06316cdca192,true,51.0,BM_Test,true,f5839ade-5968-4879-a824-90b5fb3552cd,,,,cosPhiFixed:{(0.00,1.00)},2fdca5f1-c11b-4169-a695-4c98f0e0a84a
2+
a3b7576b-cac7-4350-90ff-06316cdca192,true,51.0,BM_Test,true,f5839ade-5968-4879-a824-90b5fb3552cd,,,,"cosPhiFixed:{(0.00,1.00)}",2fdca5f1-c11b-4169-a695-4c98f0e0a84a
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"uuid","cos_phi_rated","id","node","operates_from","operates_until","operator","q_characteristics","charging_points","type","location_type","v2gSupport"
2-
06a14909-366e-4e94-a593-1016e1455b30,0.9,test_evcs_1,5f1c776c-6935-40f7-ba9e-60646e08992b,,,,cosPhiFixed:{(0.00,1.0)},4,ChargingStationType1,HOME,false
3-
104acdaa-5dc5-4197-aed2-2fddb3c4f237,0.9,test_evcs_2,ed4697fd-016c-40c2-a66b-e793878dadea,,,,cosPhiFixed:{(0.00,1.0)},4,ChargingStationType1,HOME,false
2+
06a14909-366e-4e94-a593-1016e1455b30,0.9,test_evcs_1,5f1c776c-6935-40f7-ba9e-60646e08992b,,,,"cosPhiFixed:{(0.00,1.0)}",4,ChargingStationType1,HOME,false
3+
104acdaa-5dc5-4197-aed2-2fddb3c4f237,0.9,test_evcs_2,ed4697fd-016c-40c2-a66b-e793878dadea,,,,"cosPhiFixed:{(0.00,1.0)}",4,ChargingStationType1,HOME,false
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"uuid","cos_phi_rated","id","node","operates_from","operates_until","operator","q_characteristics","s_rated"
2-
9abe950d-362e-4efe-b686-500f84d8f368,0.9,test_feed_in,5f1c776c-6935-40f7-ba9e-60646e08992b,,,,cosPhiFixed:{(0.00,0.95)},200.0
2+
9abe950d-362e-4efe-b686-500f84d8f368,0.9,test_feed_in,5f1c776c-6935-40f7-ba9e-60646e08992b,,,,"cosPhiFixed:{(0.00,0.95)}",200.0

0 commit comments

Comments
 (0)