mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
CE-781 Add option to treat CSV headers as field names (rather than working with a table's fields)
This commit is contained in:
@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.AbstractQFieldMapping;
|
import com.kingsrook.qqq.backend.core.model.actions.shared.mapping.AbstractQFieldMapping;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
|
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
@ -133,11 +134,17 @@ public class CsvToQRecordAdapter
|
|||||||
CSVFormat.DEFAULT
|
CSVFormat.DEFAULT
|
||||||
.withFirstRecordAsHeader()
|
.withFirstRecordAsHeader()
|
||||||
.withIgnoreHeaderCase()
|
.withIgnoreHeaderCase()
|
||||||
|
.withIgnoreEmptyLines()
|
||||||
.withTrim());
|
.withTrim());
|
||||||
|
|
||||||
List<String> headers = csvParser.getHeaderNames();
|
List<String> headers = csvParser.getHeaderNames();
|
||||||
headers = makeHeadersUnique(headers);
|
headers = makeHeadersUnique(headers);
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// used by csv-headers-as-field-names //
|
||||||
|
////////////////////////////////////////
|
||||||
|
Map<String, QFieldMetaData> csvHeaderFieldMapping = buildCsvHeaderFieldMappingIfNeeded(inputWrapper, headers);
|
||||||
|
|
||||||
Iterator<CSVRecord> csvIterator = csvParser.iterator();
|
Iterator<CSVRecord> csvIterator = csvParser.iterator();
|
||||||
int recordCount = 0;
|
int recordCount = 0;
|
||||||
while(csvIterator.hasNext())
|
while(csvIterator.hasNext())
|
||||||
@ -160,12 +167,28 @@ public class CsvToQRecordAdapter
|
|||||||
QRecord qRecord = new QRecord();
|
QRecord qRecord = new QRecord();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if(inputWrapper.getCsvHeadersAsFieldNames())
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// in csv-headers-as-field-names mode, don't mess with table, and don't do any mapping //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
for(Map.Entry<String, String> entry : csvValues.entrySet())
|
||||||
|
{
|
||||||
|
setValue(inputWrapper, qRecord, csvHeaderFieldMapping.get(entry.getKey()), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
///////////////////////////////////////
|
||||||
|
// otherwise, fields come from table //
|
||||||
|
///////////////////////////////////////
|
||||||
for(QFieldMetaData field : table.getFields().values())
|
for(QFieldMetaData field : table.getFields().values())
|
||||||
{
|
{
|
||||||
String fieldSource = mapping == null ? field.getName() : String.valueOf(mapping.getFieldSource(field.getName()));
|
String fieldSource = mapping == null ? field.getName() : String.valueOf(mapping.getFieldSource(field.getName()));
|
||||||
fieldSource = adjustHeaderCase(fieldSource, inputWrapper);
|
fieldSource = adjustHeaderCase(fieldSource, inputWrapper);
|
||||||
setValue(inputWrapper, qRecord, field, csvValues.get(fieldSource));
|
setValue(inputWrapper, qRecord, field, csvValues.get(fieldSource));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runRecordCustomizer(recordCustomizer, qRecord);
|
runRecordCustomizer(recordCustomizer, qRecord);
|
||||||
}
|
}
|
||||||
@ -247,6 +270,26 @@ public class CsvToQRecordAdapter
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private Map<String, QFieldMetaData> buildCsvHeaderFieldMappingIfNeeded(InputWrapper inputWrapper, List<String> headers)
|
||||||
|
{
|
||||||
|
Map<String, QFieldMetaData> csvHeaderFieldMapping = null;
|
||||||
|
if(inputWrapper.getCsvHeadersAsFieldNames())
|
||||||
|
{
|
||||||
|
csvHeaderFieldMapping = new HashMap<>();
|
||||||
|
for(String header : headers)
|
||||||
|
{
|
||||||
|
header = adjustHeaderCase(header, inputWrapper);
|
||||||
|
csvHeaderFieldMapping.put(header, new QFieldMetaData(header, QFieldType.STRING));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return csvHeaderFieldMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -377,6 +420,7 @@ public class CsvToQRecordAdapter
|
|||||||
private boolean doCorrectValueTypes = false;
|
private boolean doCorrectValueTypes = false;
|
||||||
|
|
||||||
private boolean caseSensitiveHeaders = false;
|
private boolean caseSensitiveHeaders = false;
|
||||||
|
private boolean csvHeadersAsFieldNames = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -618,6 +662,40 @@ public class CsvToQRecordAdapter
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for csvHeadersAsFieldNames
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getCsvHeadersAsFieldNames()
|
||||||
|
{
|
||||||
|
return csvHeadersAsFieldNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for csvHeadersAsFieldNames
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setCsvHeadersAsFieldNames(boolean csvHeadersAsFieldNames)
|
||||||
|
{
|
||||||
|
this.csvHeadersAsFieldNames = csvHeadersAsFieldNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for csvHeadersAsFieldNames
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public InputWrapper withCsvHeadersAsFieldNames(boolean csvHeadersAsFieldNames)
|
||||||
|
{
|
||||||
|
this.csvHeadersAsFieldNames = csvHeadersAsFieldNames;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for doCorrectValueTypes
|
** Getter for doCorrectValueTypes
|
||||||
**
|
**
|
||||||
|
@ -465,4 +465,60 @@ class CsvToQRecordAdapterTest extends BaseTest
|
|||||||
assertThat(qRecord.getErrors().get(0).toString()).isEqualTo("Error parsing line #2: Value [green] could not be converted to an Integer.");
|
assertThat(qRecord.getErrors().get(0).toString()).isEqualTo("Error parsing line #2: Value [green] could not be converted to an Integer.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testCsvHeadersAsFields() throws QException
|
||||||
|
{
|
||||||
|
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||||
|
csvToQRecordAdapter.buildRecordsFromCsv(new CsvToQRecordAdapter.InputWrapper()
|
||||||
|
.withCsvHeadersAsFieldNames(true)
|
||||||
|
.withCaseSensitiveHeaders(true)
|
||||||
|
.withCsv("""
|
||||||
|
firstName,birthDate,favoriteShapeId
|
||||||
|
John,1980,1
|
||||||
|
Paul,1970-06-15,green
|
||||||
|
"""));
|
||||||
|
|
||||||
|
List<QRecord> qRecords = csvToQRecordAdapter.getRecordList();
|
||||||
|
|
||||||
|
QRecord qRecord = qRecords.get(0);
|
||||||
|
assertEquals("John", qRecord.getValue("firstName"));
|
||||||
|
assertEquals("1980", qRecord.getValue("birthDate"));
|
||||||
|
assertEquals("1", qRecord.getValue("favoriteShapeId"));
|
||||||
|
|
||||||
|
qRecord = qRecords.get(1);
|
||||||
|
assertEquals("Paul", qRecord.getValue("firstName"));
|
||||||
|
assertEquals("1970-06-15", qRecord.getValue("birthDate"));
|
||||||
|
assertEquals("green", qRecord.getValue("favoriteShapeId"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testCsvHeadersAsFieldsDuplicatedNames() throws QException
|
||||||
|
{
|
||||||
|
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||||
|
csvToQRecordAdapter.buildRecordsFromCsv(new CsvToQRecordAdapter.InputWrapper()
|
||||||
|
.withCsvHeadersAsFieldNames(true)
|
||||||
|
.withCaseSensitiveHeaders(true)
|
||||||
|
.withCsv("""
|
||||||
|
orderId,sku,sku
|
||||||
|
10001,BASIC1,BASIC2
|
||||||
|
"""));
|
||||||
|
|
||||||
|
List<QRecord> qRecords = csvToQRecordAdapter.getRecordList();
|
||||||
|
|
||||||
|
QRecord qRecord = qRecords.get(0);
|
||||||
|
assertEquals("10001", qRecord.getValue("orderId"));
|
||||||
|
assertEquals("BASIC1", qRecord.getValue("sku"));
|
||||||
|
assertEquals("BASIC2", qRecord.getValue("sku 2"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user