mirror of
https://gitea.gofwd.group/Forward_Group/ballistic-builder-spring.git
synced 2025-12-06 11:06:45 -05:00
readme docs
This commit is contained in:
@@ -320,6 +320,77 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
||||
// Feed reading + brand resolution
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Open a Reader for either an HTTP(S) URL or a local file path.
|
||||
*/
|
||||
private Reader openFeedReader(String feedUrl) throws java.io.IOException {
|
||||
if (feedUrl.startsWith("http://") || feedUrl.startsWith("https://")) {
|
||||
return new InputStreamReader(new URL(feedUrl).openStream(), StandardCharsets.UTF_8);
|
||||
} else {
|
||||
return java.nio.file.Files.newBufferedReader(
|
||||
java.nio.file.Paths.get(feedUrl),
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try a few common delimiters (tab, comma, semicolon) and pick the one
|
||||
* that yields the expected AvantLink-style header set.
|
||||
*/
|
||||
private CSVFormat detectCsvFormat(String feedUrl) throws Exception {
|
||||
char[] delimiters = new char[]{'\t', ',', ';'};
|
||||
java.util.List<String> requiredHeaders =
|
||||
java.util.Arrays.asList("SKU", "Manufacturer Id", "Brand Name", "Product Name");
|
||||
|
||||
Exception lastException = null;
|
||||
|
||||
for (char delimiter : delimiters) {
|
||||
try (Reader reader = openFeedReader(feedUrl);
|
||||
CSVParser parser = CSVFormat.DEFAULT.builder()
|
||||
.setDelimiter(delimiter)
|
||||
.setHeader()
|
||||
.setSkipHeaderRecord(true)
|
||||
.setIgnoreSurroundingSpaces(true)
|
||||
.setTrim(true)
|
||||
.build()
|
||||
.parse(reader)) {
|
||||
|
||||
Map<String, Integer> headerMap = parser.getHeaderMap();
|
||||
if (headerMap != null && headerMap.keySet().containsAll(requiredHeaders)) {
|
||||
System.out.println(
|
||||
"IMPORT >>> detected delimiter '" +
|
||||
(delimiter == '\t' ? "\\t" : String.valueOf(delimiter)) +
|
||||
"' for feed: " + feedUrl
|
||||
);
|
||||
|
||||
return CSVFormat.DEFAULT.builder()
|
||||
.setDelimiter(delimiter)
|
||||
.setHeader()
|
||||
.setSkipHeaderRecord(true)
|
||||
.setIgnoreSurroundingSpaces(true)
|
||||
.setTrim(true)
|
||||
.build();
|
||||
} else if (headerMap != null) {
|
||||
System.out.println(
|
||||
"IMPORT !!! delimiter '" +
|
||||
(delimiter == '\t' ? "\\t" : String.valueOf(delimiter)) +
|
||||
"' produced headers: " + headerMap.keySet()
|
||||
);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
lastException = ex;
|
||||
System.out.println("IMPORT !!! error probing delimiter '" + delimiter +
|
||||
"' for " + feedUrl + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (lastException != null) {
|
||||
throw lastException;
|
||||
}
|
||||
throw new RuntimeException("Could not auto-detect delimiter for feed: " + feedUrl);
|
||||
}
|
||||
|
||||
private List<MerchantFeedRow> readFeedRowsForMerchant(Merchant merchant) {
|
||||
String rawFeedUrl = merchant.getFeedUrl();
|
||||
if (rawFeedUrl == null || rawFeedUrl.isBlank()) {
|
||||
@@ -331,68 +402,41 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
||||
|
||||
List<MerchantFeedRow> rows = new ArrayList<>();
|
||||
|
||||
try (Reader baseReader = (feedUrl.startsWith("http://") || feedUrl.startsWith("https://"))
|
||||
? new InputStreamReader(new URL(feedUrl).openStream(), StandardCharsets.UTF_8)
|
||||
: java.nio.file.Files.newBufferedReader(java.nio.file.Paths.get(feedUrl), StandardCharsets.UTF_8);
|
||||
BufferedReader reader = new BufferedReader(baseReader)) {
|
||||
try {
|
||||
// Auto-detect delimiter (TSV/CSV/semicolon) based on header row
|
||||
CSVFormat format = detectCsvFormat(feedUrl);
|
||||
|
||||
// --- Step 1: peek at the first line to detect delimiter ---
|
||||
reader.mark(10_000);
|
||||
String firstLine = reader.readLine();
|
||||
reader.reset();
|
||||
try (Reader reader = openFeedReader(feedUrl);
|
||||
CSVParser parser = new CSVParser(reader, format)) {
|
||||
|
||||
if (firstLine == null || firstLine.isEmpty()) {
|
||||
throw new RuntimeException("Empty feed received from " + feedUrl);
|
||||
}
|
||||
System.out.println("IMPORT >>> detected headers: " + parser.getHeaderMap().keySet());
|
||||
|
||||
// --- Step 2: detect delimiter (TSV vs CSV) ---
|
||||
char delimiter;
|
||||
if (firstLine.contains("\t")) {
|
||||
delimiter = '\t'; // TSV (AvantLink-style)
|
||||
} else if (firstLine.contains(",")) {
|
||||
delimiter = ','; // CSV
|
||||
} else {
|
||||
// Fallback: default to comma
|
||||
delimiter = ',';
|
||||
}
|
||||
|
||||
// --- Step 3: build CSVFormat with detected delimiter ---
|
||||
CSVFormat format = CSVFormat.DEFAULT.builder()
|
||||
.setDelimiter(delimiter)
|
||||
.setHeader()
|
||||
.setSkipHeaderRecord(true)
|
||||
.setIgnoreSurroundingSpaces(true)
|
||||
.setTrim(true)
|
||||
.build();
|
||||
|
||||
// --- Step 4: parse the rows into MerchantFeedRow records ---
|
||||
try (CSVParser parser = new CSVParser(reader, format)) {
|
||||
for (CSVRecord rec : parser) {
|
||||
MerchantFeedRow row = new MerchantFeedRow(
|
||||
rec.get("SKU"),
|
||||
rec.get("Manufacturer Id"),
|
||||
rec.get("Brand Name"),
|
||||
rec.get("Product Name"),
|
||||
rec.get("Long Description"),
|
||||
rec.get("Short Description"),
|
||||
rec.get("Department"),
|
||||
rec.get("Category"),
|
||||
rec.get("SubCategory"),
|
||||
rec.get("Thumb URL"),
|
||||
rec.get("Image URL"),
|
||||
rec.get("Buy Link"),
|
||||
rec.get("Keywords"),
|
||||
rec.get("Reviews"),
|
||||
parseBigDecimal(rec.get("Retail Price")),
|
||||
parseBigDecimal(rec.get("Sale Price")),
|
||||
rec.get("Brand Page Link"),
|
||||
rec.get("Brand Logo Image"),
|
||||
rec.get("Product Page View Tracking"),
|
||||
rec.get("Variants XML"),
|
||||
rec.get("Medium Image URL"),
|
||||
rec.get("Product Content Widget"),
|
||||
rec.get("Google Categorization"),
|
||||
rec.get("Item Based Commission")
|
||||
getCsvValue(rec, "SKU"),
|
||||
getCsvValue(rec, "Manufacturer Id"),
|
||||
getCsvValue(rec, "Brand Name"),
|
||||
getCsvValue(rec, "Product Name"),
|
||||
getCsvValue(rec, "Long Description"),
|
||||
getCsvValue(rec, "Short Description"),
|
||||
getCsvValue(rec, "Department"),
|
||||
getCsvValue(rec, "Category"),
|
||||
getCsvValue(rec, "SubCategory"),
|
||||
getCsvValue(rec, "Thumb URL"),
|
||||
getCsvValue(rec, "Image URL"),
|
||||
getCsvValue(rec, "Buy Link"),
|
||||
getCsvValue(rec, "Keywords"),
|
||||
getCsvValue(rec, "Reviews"),
|
||||
parseBigDecimal(getCsvValue(rec, "Retail Price")),
|
||||
parseBigDecimal(getCsvValue(rec, "Sale Price")),
|
||||
getCsvValue(rec, "Brand Page Link"),
|
||||
getCsvValue(rec, "Brand Logo Image"),
|
||||
getCsvValue(rec, "Product Page View Tracking"),
|
||||
null,
|
||||
getCsvValue(rec, "Medium Image URL"),
|
||||
getCsvValue(rec, "Product Content Widget"),
|
||||
getCsvValue(rec, "Google Categorization"),
|
||||
getCsvValue(rec, "Item Based Commission")
|
||||
);
|
||||
|
||||
rows.add(row);
|
||||
@@ -435,6 +479,28 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely get a column value by header name. If the record is "short"
|
||||
* (fewer values than headers) or the header is missing, return null
|
||||
* instead of throwing IllegalArgumentException.
|
||||
*/
|
||||
private String getCsvValue(CSVRecord rec, String header) {
|
||||
if (rec == null || header == null) {
|
||||
return null;
|
||||
}
|
||||
if (!rec.isMapped(header)) {
|
||||
// Header not present at all
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return rec.get(header);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
System.out.println("IMPORT !!! short record #" + rec.getRecordNumber()
|
||||
+ " missing column '" + header + "', treating as null");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Misc helpers
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user