u
This commit is contained in:
@@ -299,7 +299,24 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
let processedCount = 0;
|
let processedCount = 0;
|
||||||
let skippedCount = 0;
|
let skippedCount = 0;
|
||||||
|
|
||||||
// Track products with missing data for logging
|
// Track skip reasons with counts and product lists
|
||||||
|
const skipReasons = {
|
||||||
|
noProductOrSeoName: { count: 0, products: [] },
|
||||||
|
excludedCategory: { count: 0, products: [] },
|
||||||
|
excludedTermsTitle: { count: 0, products: [] },
|
||||||
|
excludedTermsDescription: { count: 0, products: [] },
|
||||||
|
missingGTIN: { count: 0, products: [] },
|
||||||
|
invalidGTINChecksum: { count: 0, products: [] },
|
||||||
|
missingPicture: { count: 0, products: [] },
|
||||||
|
missingWeight: { count: 0, products: [] },
|
||||||
|
insufficientDescription: { count: 0, products: [] },
|
||||||
|
nameTooShort: { count: 0, products: [] },
|
||||||
|
outOfStock: { count: 0, products: [] },
|
||||||
|
zeroPriceOrInvalid: { count: 0, products: [] },
|
||||||
|
processingError: { count: 0, products: [] }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Legacy arrays for backward compatibility
|
||||||
const productsNeedingWeight = [];
|
const productsNeedingWeight = [];
|
||||||
const productsNeedingDescription = [];
|
const productsNeedingDescription = [];
|
||||||
|
|
||||||
@@ -317,6 +334,12 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
// Skip products without essential data
|
// Skip products without essential data
|
||||||
if (!product || !product.seoName) {
|
if (!product || !product.seoName) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.noProductOrSeoName.count++;
|
||||||
|
skipReasons.noProductOrSeoName.products.push({
|
||||||
|
id: product?.articleNumber || 'N/A',
|
||||||
|
name: product?.name || 'N/A',
|
||||||
|
url: product?.seoName ? `/Artikel/${product.seoName}` : 'N/A'
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,6 +347,13 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
const productCategoryId = product.categoryId || product.category_id || product.category || null;
|
const productCategoryId = product.categoryId || product.category_id || product.category || null;
|
||||||
if (productCategoryId && skipCategoryIds.includes(parseInt(productCategoryId))) {
|
if (productCategoryId && skipCategoryIds.includes(parseInt(productCategoryId))) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.excludedCategory.count++;
|
||||||
|
skipReasons.excludedCategory.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: product.name || 'N/A',
|
||||||
|
categoryId: productCategoryId,
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,20 +369,42 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check title for excluded terms
|
// Check title for excluded terms
|
||||||
if (excludedTerms.title.some(term => productTitle.includes(term))) {
|
const excludedTitleTerm = excludedTerms.title.find(term => productTitle.includes(term));
|
||||||
|
if (excludedTitleTerm) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.excludedTermsTitle.count++;
|
||||||
|
skipReasons.excludedTermsTitle.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: product.name || 'N/A',
|
||||||
|
term: excludedTitleTerm,
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check description for excluded terms
|
// Check description for excluded terms
|
||||||
if (excludedTerms.description.some(term => productDescription.toLowerCase().includes(term))) {
|
const excludedDescTerm = excludedTerms.description.find(term => productDescription.toLowerCase().includes(term));
|
||||||
|
if (excludedDescTerm) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.excludedTermsDescription.count++;
|
||||||
|
skipReasons.excludedTermsDescription.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: product.name || 'N/A',
|
||||||
|
term: excludedDescTerm,
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip products without GTIN or with invalid GTIN
|
// Skip products without GTIN or with invalid GTIN
|
||||||
if (!product.gtin || !product.gtin.toString().trim()) {
|
if (!product.gtin || !product.gtin.toString().trim()) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.missingGTIN.count++;
|
||||||
|
skipReasons.missingGTIN.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: product.name || 'N/A',
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,23 +453,39 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
|
|
||||||
if (!isValidGTIN(gtinString)) {
|
if (!isValidGTIN(gtinString)) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.invalidGTINChecksum.count++;
|
||||||
|
skipReasons.invalidGTINChecksum.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: product.name || 'N/A',
|
||||||
|
gtin: gtinString,
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip products without pictures
|
// Skip products without pictures
|
||||||
if (!product.pictureList || !product.pictureList.trim()) {
|
if (!product.pictureList || !product.pictureList.trim()) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.missingPicture.count++;
|
||||||
|
skipReasons.missingPicture.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: product.name || 'N/A',
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if product has weight data - validate BEFORE building XML
|
// Check if product has weight data - validate BEFORE building XML
|
||||||
if (!product.weight || isNaN(product.weight)) {
|
if (!product.weight || isNaN(product.weight)) {
|
||||||
// Track products without weight
|
// Track products without weight
|
||||||
productsNeedingWeight.push({
|
const productInfo = {
|
||||||
id: product.articleNumber || product.seoName,
|
id: product.articleNumber || product.seoName,
|
||||||
name: product.name || 'Unnamed',
|
name: product.name || 'Unnamed',
|
||||||
url: `/Artikel/${product.seoName}`
|
url: `/Artikel/${product.seoName}`
|
||||||
});
|
};
|
||||||
|
productsNeedingWeight.push(productInfo);
|
||||||
|
skipReasons.missingWeight.count++;
|
||||||
|
skipReasons.missingWeight.products.push(productInfo);
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -425,12 +493,15 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
// Check if description is missing or too short (less than 20 characters) - skip if insufficient
|
// Check if description is missing or too short (less than 20 characters) - skip if insufficient
|
||||||
const originalDescription = productDescription ? cleanTextContent(productDescription) : '';
|
const originalDescription = productDescription ? cleanTextContent(productDescription) : '';
|
||||||
if (!originalDescription || originalDescription.length < 20) {
|
if (!originalDescription || originalDescription.length < 20) {
|
||||||
productsNeedingDescription.push({
|
const productInfo = {
|
||||||
id: product.articleNumber || product.seoName,
|
id: product.articleNumber || product.seoName,
|
||||||
name: product.name || 'Unnamed',
|
name: product.name || 'Unnamed',
|
||||||
currentDescription: originalDescription || 'NONE',
|
currentDescription: originalDescription || 'NONE',
|
||||||
url: `/Artikel/${product.seoName}`
|
url: `/Artikel/${product.seoName}`
|
||||||
});
|
};
|
||||||
|
productsNeedingDescription.push(productInfo);
|
||||||
|
skipReasons.insufficientDescription.count++;
|
||||||
|
skipReasons.insufficientDescription.products.push(productInfo);
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -446,6 +517,13 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
// Validate essential fields
|
// Validate essential fields
|
||||||
if (!cleanName || cleanName.length < 2) {
|
if (!cleanName || cleanName.length < 2) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.nameTooShort.count++;
|
||||||
|
skipReasons.nameTooShort.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: rawName,
|
||||||
|
cleanedName: cleanName,
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,6 +548,12 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
// Skip products that are out of stock
|
// Skip products that are out of stock
|
||||||
if (!product.available) {
|
if (!product.available) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.outOfStock.count++;
|
||||||
|
skipReasons.outOfStock.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: product.name || 'N/A',
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,6 +565,13 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
// Skip products with price == 0
|
// Skip products with price == 0
|
||||||
if (!product.price || parseFloat(product.price) === 0) {
|
if (!product.price || parseFloat(product.price) === 0) {
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.zeroPriceOrInvalid.count++;
|
||||||
|
skipReasons.zeroPriceOrInvalid.products.push({
|
||||||
|
id: product.articleNumber || product.seoName,
|
||||||
|
name: product.name || 'N/A',
|
||||||
|
price: product.price,
|
||||||
|
url: `/Artikel/${product.seoName}`
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +638,13 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
} catch (itemError) {
|
} catch (itemError) {
|
||||||
console.log(` ⚠️ Skipped product ${index + 1}: ${itemError.message}`);
|
console.log(` ⚠️ Skipped product ${index + 1}: ${itemError.message}`);
|
||||||
skippedCount++;
|
skippedCount++;
|
||||||
|
skipReasons.processingError.count++;
|
||||||
|
skipReasons.processingError.products.push({
|
||||||
|
id: product?.articleNumber || product?.seoName || 'N/A',
|
||||||
|
name: product?.name || 'N/A',
|
||||||
|
error: itemError.message,
|
||||||
|
url: product?.seoName ? `/Artikel/${product.seoName}` : 'N/A'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -554,7 +652,43 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
</channel>
|
</channel>
|
||||||
</rss>`;
|
</rss>`;
|
||||||
|
|
||||||
console.log(` 📊 Processing summary: ${processedCount} products included, ${skippedCount} skipped`);
|
console.log(`\n 📊 Processing summary: ${processedCount} products included, ${skippedCount} skipped`);
|
||||||
|
|
||||||
|
// Display skip reason totals
|
||||||
|
console.log(`\n 📋 Skip Reasons Breakdown:`);
|
||||||
|
console.log(` ────────────────────────────────────────────────────────────`);
|
||||||
|
|
||||||
|
const skipReasonLabels = {
|
||||||
|
noProductOrSeoName: 'No Product or SEO Name',
|
||||||
|
excludedCategory: 'Excluded Category',
|
||||||
|
excludedTermsTitle: 'Excluded Terms in Title',
|
||||||
|
excludedTermsDescription: 'Excluded Terms in Description',
|
||||||
|
missingGTIN: 'Missing GTIN',
|
||||||
|
invalidGTINChecksum: 'Invalid GTIN Checksum',
|
||||||
|
missingPicture: 'Missing Picture',
|
||||||
|
missingWeight: 'Missing Weight',
|
||||||
|
insufficientDescription: 'Insufficient Description',
|
||||||
|
nameTooShort: 'Name Too Short',
|
||||||
|
outOfStock: 'Out of Stock',
|
||||||
|
zeroPriceOrInvalid: 'Zero or Invalid Price',
|
||||||
|
processingError: 'Processing Error'
|
||||||
|
};
|
||||||
|
|
||||||
|
let hasAnySkips = false;
|
||||||
|
Object.entries(skipReasons).forEach(([key, data]) => {
|
||||||
|
if (data.count > 0) {
|
||||||
|
hasAnySkips = true;
|
||||||
|
const label = skipReasonLabels[key] || key;
|
||||||
|
console.log(` • ${label}: ${data.count}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasAnySkips) {
|
||||||
|
console.log(` ✅ No products were skipped`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(` ────────────────────────────────────────────────────────────`);
|
||||||
|
console.log(` Total: ${skippedCount} products skipped\n`);
|
||||||
|
|
||||||
// Write log files for products needing attention
|
// Write log files for products needing attention
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
@@ -565,7 +699,56 @@ const generateProductsXml = (allProductsData = [], baseUrl, config) => {
|
|||||||
fs.mkdirSync(logsDir, { recursive: true });
|
fs.mkdirSync(logsDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write missing weight log
|
// Write comprehensive skip reasons log
|
||||||
|
const skipLogPath = path.join(logsDir, `skip-reasons-${timestamp}.log`);
|
||||||
|
let skipLogContent = `# Product Skip Reasons Report
|
||||||
|
# Generated: ${new Date().toISOString()}
|
||||||
|
# Total products processed: ${processedCount}
|
||||||
|
# Total products skipped: ${skippedCount}
|
||||||
|
# Base URL: ${baseUrl}
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
Object.entries(skipReasons).forEach(([key, data]) => {
|
||||||
|
if (data.count > 0) {
|
||||||
|
const label = skipReasonLabels[key] || key;
|
||||||
|
skipLogContent += `\n## ${label} (${data.count} products)\n`;
|
||||||
|
skipLogContent += `${'='.repeat(80)}\n`;
|
||||||
|
|
||||||
|
data.products.forEach(product => {
|
||||||
|
skipLogContent += `ID: ${product.id}\n`;
|
||||||
|
skipLogContent += `Name: ${product.name}\n`;
|
||||||
|
if (product.categoryId !== undefined) {
|
||||||
|
skipLogContent += `Category ID: ${product.categoryId}\n`;
|
||||||
|
}
|
||||||
|
if (product.term !== undefined) {
|
||||||
|
skipLogContent += `Excluded Term: ${product.term}\n`;
|
||||||
|
}
|
||||||
|
if (product.gtin !== undefined) {
|
||||||
|
skipLogContent += `GTIN: ${product.gtin}\n`;
|
||||||
|
}
|
||||||
|
if (product.currentDescription !== undefined) {
|
||||||
|
skipLogContent += `Current Description: "${product.currentDescription}"\n`;
|
||||||
|
}
|
||||||
|
if (product.cleanedName !== undefined) {
|
||||||
|
skipLogContent += `Cleaned Name: "${product.cleanedName}"\n`;
|
||||||
|
}
|
||||||
|
if (product.price !== undefined) {
|
||||||
|
skipLogContent += `Price: ${product.price}\n`;
|
||||||
|
}
|
||||||
|
if (product.error !== undefined) {
|
||||||
|
skipLogContent += `Error: ${product.error}\n`;
|
||||||
|
}
|
||||||
|
skipLogContent += `URL: ${baseUrl}${product.url}\n`;
|
||||||
|
skipLogContent += `${'-'.repeat(80)}\n`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeFileSync(skipLogPath, skipLogContent, 'utf8');
|
||||||
|
console.log(` 📄 Detailed skip reasons report saved to: ${skipLogPath}`);
|
||||||
|
|
||||||
|
// Write missing weight log (for backward compatibility)
|
||||||
if (productsNeedingWeight.length > 0) {
|
if (productsNeedingWeight.length > 0) {
|
||||||
const weightLogContent = `# Products Missing Weight Data
|
const weightLogContent = `# Products Missing Weight Data
|
||||||
# Generated: ${new Date().toISOString()}
|
# Generated: ${new Date().toISOString()}
|
||||||
@@ -576,10 +759,10 @@ ${productsNeedingWeight.map(product => `${product.id}\t${product.name}\t${baseUr
|
|||||||
|
|
||||||
const weightLogPath = path.join(logsDir, `missing-weight-${timestamp}.log`);
|
const weightLogPath = path.join(logsDir, `missing-weight-${timestamp}.log`);
|
||||||
fs.writeFileSync(weightLogPath, weightLogContent, 'utf8');
|
fs.writeFileSync(weightLogPath, weightLogContent, 'utf8');
|
||||||
console.log(`\n ⚠️ Products missing weight (${productsNeedingWeight.length}) - saved to: ${weightLogPath}`);
|
console.log(` ⚠️ Products missing weight (${productsNeedingWeight.length}) - saved to: ${weightLogPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write missing description log
|
// Write missing description log (for backward compatibility)
|
||||||
if (productsNeedingDescription.length > 0) {
|
if (productsNeedingDescription.length > 0) {
|
||||||
const descLogContent = `# Products With Insufficient Description Data
|
const descLogContent = `# Products With Insufficient Description Data
|
||||||
# Generated: ${new Date().toISOString()}
|
# Generated: ${new Date().toISOString()}
|
||||||
@@ -590,7 +773,7 @@ ${productsNeedingDescription.map(product => `${product.id}\t${product.name}\t"${
|
|||||||
|
|
||||||
const descLogPath = path.join(logsDir, `missing-description-${timestamp}.log`);
|
const descLogPath = path.join(logsDir, `missing-description-${timestamp}.log`);
|
||||||
fs.writeFileSync(descLogPath, descLogContent, 'utf8');
|
fs.writeFileSync(descLogPath, descLogContent, 'utf8');
|
||||||
console.log(`\n ⚠️ Products with insufficient description (${productsNeedingDescription.length}) - saved to: ${descLogPath}`);
|
console.log(` ⚠️ Products with insufficient description (${productsNeedingDescription.length}) - saved to: ${descLogPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (productsNeedingWeight.length === 0 && productsNeedingDescription.length === 0) {
|
if (productsNeedingWeight.length === 0 && productsNeedingDescription.length === 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user