diff --git a/public/datasheets.zip b/public/datasheets.zip new file mode 100644 index 00000000..755320f2 Binary files /dev/null and b/public/datasheets.zip differ diff --git a/public/datasheets/h1z2z2-k-de.pdf b/public/datasheets/h1z2z2-k-de.pdf index b8fceb40..04c698a5 100644 Binary files a/public/datasheets/h1z2z2-k-de.pdf and b/public/datasheets/h1z2z2-k-de.pdf differ diff --git a/public/datasheets/h1z2z2-k-en.pdf b/public/datasheets/h1z2z2-k-en.pdf index 48e72b76..9165999f 100644 Binary files a/public/datasheets/h1z2z2-k-en.pdf and b/public/datasheets/h1z2z2-k-en.pdf differ diff --git a/public/datasheets/n2x2y-2-de.pdf b/public/datasheets/n2x2y-2-de.pdf index f58b511f..7482da58 100644 Binary files a/public/datasheets/n2x2y-2-de.pdf and b/public/datasheets/n2x2y-2-de.pdf differ diff --git a/public/datasheets/n2x2y-en.pdf b/public/datasheets/n2x2y-en.pdf index ed456cd9..e166f6ce 100644 Binary files a/public/datasheets/n2x2y-en.pdf and b/public/datasheets/n2x2y-en.pdf differ diff --git a/public/datasheets/n2xfk2y-de.pdf b/public/datasheets/n2xfk2y-de.pdf index 8aeec4cc..9a28df58 100644 Binary files a/public/datasheets/n2xfk2y-de.pdf and b/public/datasheets/n2xfk2y-de.pdf differ diff --git a/public/datasheets/n2xfk2y-en.pdf b/public/datasheets/n2xfk2y-en.pdf index 0febfd3c..d1fca04d 100644 Binary files a/public/datasheets/n2xfk2y-en.pdf and b/public/datasheets/n2xfk2y-en.pdf differ diff --git a/public/datasheets/n2xfkld2y-de.pdf b/public/datasheets/n2xfkld2y-de.pdf index 2045899e..a2931c52 100644 Binary files a/public/datasheets/n2xfkld2y-de.pdf and b/public/datasheets/n2xfkld2y-de.pdf differ diff --git a/public/datasheets/n2xfkld2y-en.pdf b/public/datasheets/n2xfkld2y-en.pdf index 22fe0b79..74f37830 100644 Binary files a/public/datasheets/n2xfkld2y-en.pdf and b/public/datasheets/n2xfkld2y-en.pdf differ diff --git a/public/datasheets/n2xs2y-2-de.pdf b/public/datasheets/n2xs2y-2-de.pdf index a1423373..0c695d5c 100644 Binary files a/public/datasheets/n2xs2y-2-de.pdf and b/public/datasheets/n2xs2y-2-de.pdf differ diff --git a/public/datasheets/n2xs2y-en.pdf b/public/datasheets/n2xs2y-en.pdf index 4a699881..c9b9dfee 100644 Binary files a/public/datasheets/n2xs2y-en.pdf and b/public/datasheets/n2xs2y-en.pdf differ diff --git a/public/datasheets/n2xsf2y-2-de.pdf b/public/datasheets/n2xsf2y-2-de.pdf index e2d170b9..ef4a2301 100644 Binary files a/public/datasheets/n2xsf2y-2-de.pdf and b/public/datasheets/n2xsf2y-2-de.pdf differ diff --git a/public/datasheets/n2xsf2y-en.pdf b/public/datasheets/n2xsf2y-en.pdf index 6794ab32..7204ed94 100644 Binary files a/public/datasheets/n2xsf2y-en.pdf and b/public/datasheets/n2xsf2y-en.pdf differ diff --git a/public/datasheets/n2xsfl2y-2-de.pdf b/public/datasheets/n2xsfl2y-2-de.pdf index cf08ceb1..b9036522 100644 Binary files a/public/datasheets/n2xsfl2y-2-de.pdf and b/public/datasheets/n2xsfl2y-2-de.pdf differ diff --git a/public/datasheets/n2xsfl2y-3-en.pdf b/public/datasheets/n2xsfl2y-3-en.pdf index af00fca4..5cd9c874 100644 Binary files a/public/datasheets/n2xsfl2y-3-en.pdf and b/public/datasheets/n2xsfl2y-3-en.pdf differ diff --git a/public/datasheets/n2xsfl2y-de.pdf b/public/datasheets/n2xsfl2y-de.pdf index 6c78a38b..aacf0b7e 100644 Binary files a/public/datasheets/n2xsfl2y-de.pdf and b/public/datasheets/n2xsfl2y-de.pdf differ diff --git a/public/datasheets/n2xsfl2y-en.pdf b/public/datasheets/n2xsfl2y-en.pdf index 35225403..052dacc4 100644 Binary files a/public/datasheets/n2xsfl2y-en.pdf and b/public/datasheets/n2xsfl2y-en.pdf differ diff --git a/public/datasheets/n2xsy-2-de.pdf b/public/datasheets/n2xsy-2-de.pdf index 92effe2b..500b6eb2 100644 Binary files a/public/datasheets/n2xsy-2-de.pdf and b/public/datasheets/n2xsy-2-de.pdf differ diff --git a/public/datasheets/n2xsy-en.pdf b/public/datasheets/n2xsy-en.pdf index fcf21312..41020651 100644 Binary files a/public/datasheets/n2xsy-en.pdf and b/public/datasheets/n2xsy-en.pdf differ diff --git a/public/datasheets/n2xy-2-de.pdf b/public/datasheets/n2xy-2-de.pdf index f5c7061f..586c22eb 100644 Binary files a/public/datasheets/n2xy-2-de.pdf and b/public/datasheets/n2xy-2-de.pdf differ diff --git a/public/datasheets/n2xy-en.pdf b/public/datasheets/n2xy-en.pdf index 578807b2..835dbd5e 100644 Binary files a/public/datasheets/n2xy-en.pdf and b/public/datasheets/n2xy-en.pdf differ diff --git a/public/datasheets/na2x2y-2-de.pdf b/public/datasheets/na2x2y-2-de.pdf index 4a389157..e0f2a362 100644 Binary files a/public/datasheets/na2x2y-2-de.pdf and b/public/datasheets/na2x2y-2-de.pdf differ diff --git a/public/datasheets/na2x2y-en.pdf b/public/datasheets/na2x2y-en.pdf index 574f0e3e..f91d617b 100644 Binary files a/public/datasheets/na2x2y-en.pdf and b/public/datasheets/na2x2y-en.pdf differ diff --git a/public/datasheets/na2xfk2y-de.pdf b/public/datasheets/na2xfk2y-de.pdf index 22ae93ba..cbff8e51 100644 Binary files a/public/datasheets/na2xfk2y-de.pdf and b/public/datasheets/na2xfk2y-de.pdf differ diff --git a/public/datasheets/na2xfk2y-en.pdf b/public/datasheets/na2xfk2y-en.pdf index a80cdb47..922e5d6f 100644 Binary files a/public/datasheets/na2xfk2y-en.pdf and b/public/datasheets/na2xfk2y-en.pdf differ diff --git a/public/datasheets/na2xfkld2y-de.pdf b/public/datasheets/na2xfkld2y-de.pdf index c0edfb26..c67d593a 100644 Binary files a/public/datasheets/na2xfkld2y-de.pdf and b/public/datasheets/na2xfkld2y-de.pdf differ diff --git a/public/datasheets/na2xfkld2y-en.pdf b/public/datasheets/na2xfkld2y-en.pdf index a6105f8e..74e8bcec 100644 Binary files a/public/datasheets/na2xfkld2y-en.pdf and b/public/datasheets/na2xfkld2y-en.pdf differ diff --git a/public/datasheets/na2xs2y-2-de.pdf b/public/datasheets/na2xs2y-2-de.pdf index f6e95b5b..09446948 100644 Binary files a/public/datasheets/na2xs2y-2-de.pdf and b/public/datasheets/na2xs2y-2-de.pdf differ diff --git a/public/datasheets/na2xs2y-en.pdf b/public/datasheets/na2xs2y-en.pdf index 3ee5127a..ca597a05 100644 Binary files a/public/datasheets/na2xs2y-en.pdf and b/public/datasheets/na2xs2y-en.pdf differ diff --git a/public/datasheets/na2xsf2y-2-de.pdf b/public/datasheets/na2xsf2y-2-de.pdf index d800de9a..0dbc3f27 100644 Binary files a/public/datasheets/na2xsf2y-2-de.pdf and b/public/datasheets/na2xsf2y-2-de.pdf differ diff --git a/public/datasheets/na2xsf2y-en.pdf b/public/datasheets/na2xsf2y-en.pdf index 49c2edd5..31ca407c 100644 Binary files a/public/datasheets/na2xsf2y-en.pdf and b/public/datasheets/na2xsf2y-en.pdf differ diff --git a/public/datasheets/na2xsfl2y-2-de.pdf b/public/datasheets/na2xsfl2y-2-de.pdf index de555fd2..e74b2d3a 100644 Binary files a/public/datasheets/na2xsfl2y-2-de.pdf and b/public/datasheets/na2xsfl2y-2-de.pdf differ diff --git a/public/datasheets/na2xsfl2y-3-en.pdf b/public/datasheets/na2xsfl2y-3-en.pdf index dd45f2fc..4b40ec86 100644 Binary files a/public/datasheets/na2xsfl2y-3-en.pdf and b/public/datasheets/na2xsfl2y-3-en.pdf differ diff --git a/public/datasheets/na2xsfl2y-de.pdf b/public/datasheets/na2xsfl2y-de.pdf index 6fb9a05c..050040f7 100644 Binary files a/public/datasheets/na2xsfl2y-de.pdf and b/public/datasheets/na2xsfl2y-de.pdf differ diff --git a/public/datasheets/na2xsfl2y-en.pdf b/public/datasheets/na2xsfl2y-en.pdf index 555d4bf1..3886795c 100644 Binary files a/public/datasheets/na2xsfl2y-en.pdf and b/public/datasheets/na2xsfl2y-en.pdf differ diff --git a/public/datasheets/na2xsy-2-de.pdf b/public/datasheets/na2xsy-2-de.pdf index 4ba3dc8a..5ecb1ab9 100644 Binary files a/public/datasheets/na2xsy-2-de.pdf and b/public/datasheets/na2xsy-2-de.pdf differ diff --git a/public/datasheets/na2xsy-en.pdf b/public/datasheets/na2xsy-en.pdf index de75e9ec..a20f50b3 100644 Binary files a/public/datasheets/na2xsy-en.pdf and b/public/datasheets/na2xsy-en.pdf differ diff --git a/public/datasheets/na2xy-2-de.pdf b/public/datasheets/na2xy-2-de.pdf index 6eddbdfd..fc8dfc83 100644 Binary files a/public/datasheets/na2xy-2-de.pdf and b/public/datasheets/na2xy-2-de.pdf differ diff --git a/public/datasheets/na2xy-en.pdf b/public/datasheets/na2xy-en.pdf index 6ddb73c0..976f2720 100644 Binary files a/public/datasheets/na2xy-en.pdf and b/public/datasheets/na2xy-en.pdf differ diff --git a/public/datasheets/nay2y-2-de.pdf b/public/datasheets/nay2y-2-de.pdf index 33b10f16..bfef5325 100644 Binary files a/public/datasheets/nay2y-2-de.pdf and b/public/datasheets/nay2y-2-de.pdf differ diff --git a/public/datasheets/nay2y-en.pdf b/public/datasheets/nay2y-en.pdf index 3eb48603..b2e21c28 100644 Binary files a/public/datasheets/nay2y-en.pdf and b/public/datasheets/nay2y-en.pdf differ diff --git a/public/datasheets/naycwy-2-de.pdf b/public/datasheets/naycwy-2-de.pdf index 96c452e0..ea66ac40 100644 Binary files a/public/datasheets/naycwy-2-de.pdf and b/public/datasheets/naycwy-2-de.pdf differ diff --git a/public/datasheets/naycwy-en.pdf b/public/datasheets/naycwy-en.pdf index 718f9d03..0fcc182e 100644 Binary files a/public/datasheets/naycwy-en.pdf and b/public/datasheets/naycwy-en.pdf differ diff --git a/public/datasheets/nayy-2-de.pdf b/public/datasheets/nayy-2-de.pdf index 36b5ed3a..22a1a7b7 100644 Binary files a/public/datasheets/nayy-2-de.pdf and b/public/datasheets/nayy-2-de.pdf differ diff --git a/public/datasheets/nayy-en.pdf b/public/datasheets/nayy-en.pdf index 1c067e47..4b17239a 100644 Binary files a/public/datasheets/nayy-en.pdf and b/public/datasheets/nayy-en.pdf differ diff --git a/public/datasheets/ny2y-2-de.pdf b/public/datasheets/ny2y-2-de.pdf index cc3e4084..fd0fe41b 100644 Binary files a/public/datasheets/ny2y-2-de.pdf and b/public/datasheets/ny2y-2-de.pdf differ diff --git a/public/datasheets/ny2y-en.pdf b/public/datasheets/ny2y-en.pdf index 30ca7fc4..3ab900ce 100644 Binary files a/public/datasheets/ny2y-en.pdf and b/public/datasheets/ny2y-en.pdf differ diff --git a/public/datasheets/nycwy-2-de.pdf b/public/datasheets/nycwy-2-de.pdf index e60e4ab2..d1089fec 100644 Binary files a/public/datasheets/nycwy-2-de.pdf and b/public/datasheets/nycwy-2-de.pdf differ diff --git a/public/datasheets/nycwy-en.pdf b/public/datasheets/nycwy-en.pdf index ff2c1cc0..0bb704b7 100644 Binary files a/public/datasheets/nycwy-en.pdf and b/public/datasheets/nycwy-en.pdf differ diff --git a/public/datasheets/nyy-2-de.pdf b/public/datasheets/nyy-2-de.pdf index b0b1fd95..9219ed49 100644 Binary files a/public/datasheets/nyy-2-de.pdf and b/public/datasheets/nyy-2-de.pdf differ diff --git a/public/datasheets/nyy-en.pdf b/public/datasheets/nyy-en.pdf index 88614917..9e107404 100644 Binary files a/public/datasheets/nyy-en.pdf and b/public/datasheets/nyy-en.pdf differ diff --git a/scripts/generate-pdf-datasheets.ts b/scripts/generate-pdf-datasheets.ts index d7cdde04..f948a629 100644 --- a/scripts/generate-pdf-datasheets.ts +++ b/scripts/generate-pdf-datasheets.ts @@ -166,8 +166,22 @@ function findExcelRowsForProduct(product: ProductData): ExcelRow[] { function guessColumnKey(row: ExcelRow, patterns: RegExp[]): string | null { const keys = Object.keys(row || {}); + + // Try pattern-based matching first for (const re of patterns) { - const k = keys.find(x => re.test(String(x))); + const k = keys.find(x => { + const key = String(x); + + // Specific exclusions to prevent wrong matches + if (re.test('conductor') && /ross section conductor/i.test(key)) return false; + if (re.test('insulation thickness') && /Diameter over insulation/i.test(key)) return false; + if (re.test('conductor') && !/^conductor$/i.test(key)) return false; + if (re.test('insulation') && !/^insulation$/i.test(key)) return false; + if (re.test('sheath') && !/^sheath$/i.test(key)) return false; + if (re.test('norm') && !/^norm$/i.test(key)) return false; + + return re.test(key); + }); if (k) return k; } return null; @@ -240,15 +254,14 @@ function ensureExcelCrossSectionAttributes(product: ProductData, locale: 'en' | return; } + // Get all technical column keys using improved detection const voltageKey = guessColumnKey(rows[0], [/rated voltage/i, /voltage rating/i, /spannungs/i, /nennspannung/i]); const outerKey = guessColumnKey(rows[0], [/outer diameter\b/i, /outer diameter.*approx/i, /outer diameter of cable/i, /außen/i]); const weightKey = guessColumnKey(rows[0], [/weight\b/i, /gewicht/i, /cable weight/i]); const dcResKey = guessColumnKey(rows[0], [/dc resistance/i, /resistance conductor/i, /leiterwiderstand/i]); - - // Additional technical columns that are often missing from WP exports. - // We add them as either constant attributes (if identical across all rows) - // or as small multi-value arrays (if they vary), so TECHNICAL DATA can render them. - const ratedVoltKey = guessColumnKey(rows[0], [/rated voltage/i, /voltage rating/i, /spannungs/i, /nennspannung/i]); + + // Additional technical columns + const ratedVoltKey = voltageKey; // Already found above const testVoltKey = guessColumnKey(rows[0], [/test voltage/i, /prüfspannung/i]); const tempRangeKey = guessColumnKey(rows[0], [/operating temperature range/i, /temperature range/i, /temperaturbereich/i]); const minLayKey = guessColumnKey(rows[0], [/minimal temperature for laying/i]); @@ -258,7 +271,35 @@ function ensureExcelCrossSectionAttributes(product: ProductData, locale: 'en' | const insThkKey = guessColumnKey(rows[0], [/nominal insulation thickness/i, /insulation thickness/i]); const sheathThkKey = guessColumnKey(rows[0], [/nominal sheath thickness/i, /minimum sheath thickness/i]); const maxResKey = guessColumnKey(rows[0], [/maximum resistance of conductor/i]); - + + // Material and specification columns + const conductorKey = guessColumnKey(rows[0], [/^conductor$/i]); + const insulationKey = guessColumnKey(rows[0], [/^insulation$/i]); + const sheathKey = guessColumnKey(rows[0], [/^sheath$/i]); + const normKey = guessColumnKey(rows[0], [/^norm$/i, /^standard$/i]); + const cprKey = guessColumnKey(rows[0], [/cpr class/i]); + const rohsKey = guessColumnKey(rows[0], [/^rohs$/i]); + const reachKey = guessColumnKey(rows[0], [/^reach$/i]); + const packagingKey = guessColumnKey(rows[0], [/^packaging$/i]); + const shapeKey = guessColumnKey(rows[0], [/shape of conductor/i]); + const flameKey = guessColumnKey(rows[0], [/flame retardant/i]); + const diamCondKey = guessColumnKey(rows[0], [/diameter conductor/i]); + const diamInsKey = guessColumnKey(rows[0], [/diameter over insulation/i]); + const diamScreenKey = guessColumnKey(rows[0], [/diameter over screen/i]); + const metalScreenKey = guessColumnKey(rows[0], [/metallic screen/i]); + const capacitanceKey = guessColumnKey(rows[0], [/capacitance/i]); + const reactanceKey = guessColumnKey(rows[0], [/reactance/i]); + const electricalStressKey = guessColumnKey(rows[0], [/electrical stress/i]); + const pullingForceKey = guessColumnKey(rows[0], [/max\. pulling force/i, /pulling force/i]); + const heatingTrefoilKey = guessColumnKey(rows[0], [/heating time constant.*trefoil/i]); + const heatingFlatKey = guessColumnKey(rows[0], [/heating time constant.*flat/i]); + const currentAirTrefoilKey = guessColumnKey(rows[0], [/current ratings in air.*trefoil/i]); + const currentAirFlatKey = guessColumnKey(rows[0], [/current ratings in air.*flat/i]); + const currentGroundTrefoilKey = guessColumnKey(rows[0], [/current ratings in ground.*trefoil/i]); + const currentGroundFlatKey = guessColumnKey(rows[0], [/current ratings in ground.*flat/i]); + const scCurrentCondKey = guessColumnKey(rows[0], [/conductor shortcircuit current/i]); + const scCurrentScreenKey = guessColumnKey(rows[0], [/screen shortcircuit current/i]); + const cfgName = locale === 'de' ? 'Anzahl der Adern und Querschnitt' : 'Number of cores and cross-section'; const cfgOptions = rows .map(r => { @@ -359,6 +400,139 @@ function ensureExcelCrossSectionAttributes(product: ProductData, locale: 'en' | key: maxResKey, }); + // Add additional technical data from Excel files + addConstOrSmallList({ + name: locale === 'de' ? 'Leiter' : 'Conductor', + existsRe: /conductor/i, + key: conductorKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Isolierung' : 'Insulation', + existsRe: /insulation/i, + key: insulationKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Mantel' : 'Sheath', + existsRe: /sheath/i, + key: sheathKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Norm' : 'Standard', + existsRe: /norm|standard|iec|vde/i, + key: normKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Leiterdurchmesser' : 'Conductor diameter', + existsRe: /diameter conductor|conductor diameter/i, + key: diamCondKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Isolierungsdurchmesser' : 'Insulation diameter', + existsRe: /diameter over insulation|diameter insulation/i, + key: diamInsKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Schirmdurchmesser' : 'Screen diameter', + existsRe: /diameter over screen|diameter screen/i, + key: diamScreenKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Metallischer Schirm' : 'Metallic screen', + existsRe: /metallic screen/i, + key: metalScreenKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Max. Zugkraft' : 'Max. pulling force', + existsRe: /max.*pulling force|pulling force/i, + key: pullingForceKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Elektrische Spannung Leiter' : 'Electrical stress conductor', + existsRe: /electrical stress conductor/i, + key: electricalStressKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Elektrische Spannung Isolierung' : 'Electrical stress insulation', + existsRe: /electrical stress insulation/i, + key: electricalStressKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Reaktanz' : 'Reactance', + existsRe: /reactance/i, + key: reactanceKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Heizzeitkonstante trefoil' : 'Heating time constant trefoil', + existsRe: /heating time constant.*trefoil/i, + key: heatingTrefoilKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Heizzeitkonstante flach' : 'Heating time constant flat', + existsRe: /heating time constant.*flat/i, + key: heatingFlatKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Flammhemmend' : 'Flame retardant', + existsRe: /flame retardant/i, + key: flameKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'CPR-Klasse' : 'CPR class', + existsRe: /cpr class/i, + key: cprKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Verpackung' : 'Packaging', + existsRe: /packaging/i, + key: packagingKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Biegeradius' : 'Bending radius', + existsRe: /bending radius/i, + key: null, // Will be found in row-specific attributes + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Leiterform' : 'Shape of conductor', + existsRe: /shape of conductor/i, + key: shapeKey, + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Isolierungsfarbe' : 'Colour of insulation', + existsRe: /colour of insulation/i, + key: null, // Will be found in row-specific attributes + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'Mantelfarbe' : 'Colour of sheath', + existsRe: /colour of sheath/i, + key: null, // Will be found in row-specific attributes + }); + + addConstOrSmallList({ + name: locale === 'de' ? 'RoHS/REACH' : 'RoHS/REACH', + existsRe: /rohs.*reach/i, + key: null, // Will be found in row-specific attributes + }); + product.attributes = attrs; if (process.env.PDF_DEBUG_EXCEL === '1') { @@ -395,6 +569,21 @@ function ensureExcelRowSpecificAttributes(product: ProductData, locale: 'en' | ' const keyScCond = guessColumnKey(sample, [/conductor shortcircuit current/i]); const keyScScreen = guessColumnKey(sample, [/screen shortcircuit current/i]); const keyBend = guessColumnKey(sample, [/bending radius/i, /min\. bending radius/i]); + + // Additional row-specific technical data + const keyConductorDiameter = guessColumnKey(sample, [/conductor diameter/i, /diameter conductor/i]); + const keyInsulationThickness = guessColumnKey(sample, [/nominal insulation thickness/i, /insulation thickness/i]); + const keySheathThickness = guessColumnKey(sample, [/nominal sheath thickness/i, /minimum sheath thickness/i, /sheath thickness/i]); + const keyCapacitance = guessColumnKey(sample, [/capacitance/i]); + const keyInductanceTrefoil = guessColumnKey(sample, [/inductance.*trefoil/i]); + const keyInductanceAirFlat = guessColumnKey(sample, [/inductance.*air.*flat/i]); + const keyInductanceGroundFlat = guessColumnKey(sample, [/inductance.*ground.*flat/i]); + const keyCurrentAirTrefoil = guessColumnKey(sample, [/current.*air.*trefoil/i]); + const keyCurrentAirFlat = guessColumnKey(sample, [/current.*air.*flat/i]); + const keyCurrentGroundTrefoil = guessColumnKey(sample, [/current.*ground.*trefoil/i]); + const keyCurrentGroundFlat = guessColumnKey(sample, [/current.*ground.*flat/i]); + const keyHeatingTimeTrefoil = guessColumnKey(sample, [/heating.*time.*trefoil/i]); + const keyHeatingTimeFlat = guessColumnKey(sample, [/heating.*time.*flat/i]); const get = (k: string | null) => rows.map(r => normalizeValue(String(r?.[k ?? ''] ?? ''))); const withUnit = (vals: string[], unit: string) => vals.map(v => (v && looksNumeric(v) ? `${v} ${unit}` : v)); @@ -511,6 +700,111 @@ function ensureExcelRowSpecificAttributes(product: ProductData, locale: 'en' | ' expectedLen: rowCount, existsRe: /bending\s*radius|biegeradius/i, }); + + // Additional row-specific technical data + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Leiterdurchmesser' : 'Conductor diameter', + options: withUnit(get(keyConductorDiameter), 'mm'), + expectedLen: rowCount, + existsRe: /conductor diameter|diameter conductor/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Isolationsdicke' : 'Insulation thickness', + options: withUnit(get(keyInsulationThickness), 'mm'), + expectedLen: rowCount, + existsRe: /insulation thickness|nominal insulation thickness/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Manteldicke' : 'Sheath thickness', + options: withUnit(get(keySheathThickness), 'mm'), + expectedLen: rowCount, + existsRe: /sheath thickness|nominal sheath thickness/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Kapazität' : 'Capacitance', + options: withUnit(get(keyCapacitance), 'μF/km'), + expectedLen: rowCount, + existsRe: /capacitance/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Induktivität trefoil' : 'Inductance trefoil', + options: withUnit(get(keyInductanceTrefoil), 'mH/km'), + expectedLen: rowCount, + existsRe: /inductance.*trefoil/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Induktivität Luft flach' : 'Inductance air flat', + options: withUnit(get(keyInductanceAirFlat), 'mH/km'), + expectedLen: rowCount, + existsRe: /inductance.*air.*flat/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Induktivität Erdreich flach' : 'Inductance ground flat', + options: withUnit(get(keyInductanceGroundFlat), 'mH/km'), + expectedLen: rowCount, + existsRe: /inductance.*ground.*flat/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Strombelastbarkeit Luft trefoil' : 'Current rating air trefoil', + options: withUnit(get(keyCurrentAirTrefoil), 'A'), + expectedLen: rowCount, + existsRe: /current.*air.*trefoil/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Strombelastbarkeit Luft flach' : 'Current rating air flat', + options: withUnit(get(keyCurrentAirFlat), 'A'), + expectedLen: rowCount, + existsRe: /current.*air.*flat/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Strombelastbarkeit Erdreich trefoil' : 'Current rating ground trefoil', + options: withUnit(get(keyCurrentGroundTrefoil), 'A'), + expectedLen: rowCount, + existsRe: /current.*ground.*trefoil/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Strombelastbarkeit Erdreich flach' : 'Current rating ground flat', + options: withUnit(get(keyCurrentGroundFlat), 'A'), + expectedLen: rowCount, + existsRe: /current.*ground.*flat/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Heizzeitkonstante trefoil' : 'Heating time constant trefoil', + options: withUnit(get(keyHeatingTimeTrefoil), 's'), + expectedLen: rowCount, + existsRe: /heating.*time.*trefoil/i, + }); + + pushRowAttrIfMissing({ + product, + name: locale === 'de' ? 'Heizzeitkonstante flach' : 'Heating time constant flat', + options: withUnit(get(keyHeatingTimeFlat), 's'), + expectedLen: rowCount, + existsRe: /heating.*time.*flat/i, + }); } function getProductUrl(product: ProductData): string | null { @@ -1972,6 +2266,7 @@ async function generatePDF(product: ProductData, locale: 'en' | 'de'): Promise(); @@ -2145,9 +2446,13 @@ async function generatePDF(product: ProductData, locale: 'en' | 'de'): Promise = []; + + if (rowOuter) availableColumns.push({ key: 'outer', label: locale === 'de' ? 'Außen-Ø' : 'Outer Ø', attr: rowOuter, unit: 'mm' }); + if (rowWeight) availableColumns.push({ key: 'weight', label: locale === 'de' ? 'Gewicht' : 'Weight', attr: rowWeight, unit: 'kg/km' }); + if (rowDcRes) availableColumns.push({ key: 'dcres', label: locale === 'de' ? 'Widerstand' : 'Resistance', attr: rowDcRes, unit: 'Ω/km' }); + if (rowCap) availableColumns.push({ key: 'cap', label: locale === 'de' ? 'Kapazität' : 'Capacitance', attr: rowCap, unit: 'μF/km' }); + if (rowCurrentAir) availableColumns.push({ key: 'curair', label: locale === 'de' ? 'Strom Luft' : 'Current Air', attr: rowCurrentAir, unit: 'A' }); + if (rowCurrentGround) availableColumns.push({ key: 'curground', label: locale === 'de' ? 'Strom Erdreich' : 'Current Ground', attr: rowCurrentGround, unit: 'A' }); + + if (availableColumns.length >= 2) { + // Use first two available columns for the preview table + const col1 = availableColumns[0]; + const col2 = availableColumns[1]; + const previewRows = configRows.map((cfg, i) => ({ config: normalizeValue(cfg), - col1: formatMaybeWithUnit(getAttrCellValue(rowOuter ?? undefined, i, rowCount), 'mm'), - col2: formatMaybeWithUnit(getAttrCellValue(rowWeight ?? undefined, i, rowCount), 'kg/km'), + col1: formatMaybeWithUnit(getAttrCellValue(col1.attr ?? undefined, i, rowCount), col1.unit), + col2: formatMaybeWithUnit(getAttrCellValue(col2.attr ?? undefined, i, rowCount), col2.unit), })); const previewTitle = locale === 'de' ? 'Konfigurationswerte (Auszug)' : 'Configuration values (excerpt)'; @@ -2203,8 +2522,8 @@ async function generatePDF(product: ProductData, locale: 'en' | 'de'): Promise page, page, @@ -2221,6 +2540,39 @@ async function generatePDF(product: ProductData, locale: 'en' | 'de'): Promise= contentMinY) y = yAfterPreview; } + + // Full table mode: show more technical columns if space allows and mode is enabled + if (pdfMode === 'full' && availableColumns.length > 2) { + // Try to show additional columns in a chunked table + const additionalColumns = availableColumns.slice(2).map(col => ({ + key: col.key, + label: col.label, + get: (i: number) => formatMaybeWithUnit(getAttrCellValue(col.attr ?? undefined, i, rowCount), col.unit), + })); + + if (additionalColumns.length > 0 && y - 100 >= contentMinY) { + y = drawTableChunked({ + title: locale === 'de' ? 'Technische Daten (alle)' : 'Technical data (all)', + configRows, + columns: additionalColumns, + locale, + newPage, + getPage: () => page, + page, + y, + margin, + contentWidth, + contentMinY, + font, + fontBold, + navy, + darkGray, + lightGray, + almostWhite, + maxDataColsPerTable: 3, + }); + } + } } else { // If there is no cross-section data, do not render the section at all. } diff --git a/test-enrichment.js b/test-enrichment.js new file mode 100644 index 00000000..b630517d --- /dev/null +++ b/test-enrichment.js @@ -0,0 +1,173 @@ +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +// Copy the key functions from the PDF script +const EXCEL_SOURCE_FILES = [ + path.join(process.cwd(), 'data/source/high-voltage.xlsx'), + path.join(process.cwd(), 'data/source/medium-voltage-KM.xlsx'), + path.join(process.cwd(), 'data/source/low-voltage-KM.xlsx'), + path.join(process.cwd(), 'data/source/solar-cables.xlsx'), +]; + +function normalizeExcelKey(value) { + return String(value || '') + .toUpperCase() + .replace(/-\d+$/g, '') + .replace(/[^A-Z0-9]+/g, ''); +} + +function loadExcelRows(filePath) { + const out = execSync(`npx -y xlsx-cli -j "${filePath}"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }); + const trimmed = out.trim(); + const jsonStart = trimmed.indexOf('['); + if (jsonStart < 0) return []; + const jsonText = trimmed.slice(jsonStart); + try { + return JSON.parse(jsonText); + } catch { + return []; + } +} + +function getExcelIndex() { + if (getExcelIndex.cached) return getExcelIndex.cached; + const idx = new Map(); + for (const file of EXCEL_SOURCE_FILES) { + if (!fs.existsSync(file)) continue; + const rows = loadExcelRows(file); + const unitsRow = rows.find(r => r && r['Part Number'] === 'Units') || null; + const units = {}; + if (unitsRow) { + for (const [k, v] of Object.entries(unitsRow)) { + if (k === 'Part Number') continue; + const unit = String(v ?? '').trim(); + if (unit) units[k] = unit; + } + } + for (const r of rows) { + const pn = r?.['Part Number']; + if (!pn || pn === 'Units') continue; + const key = normalizeExcelKey(String(pn)); + if (!key) continue; + const cur = idx.get(key); + if (!cur) { + idx.set(key, { rows: [r], units }); + } else { + cur.rows.push(r); + if (Object.keys(cur.units).length < Object.keys(units).length) cur.units = units; + } + } + } + getExcelIndex.cached = idx; + return idx; +} + +function findExcelForProduct(product) { + const idx = getExcelIndex(); + const candidates = [ + product.name, + product.slug ? product.slug.replace(/-\d+$/g, '') : '', + product.sku, + product.translationKey, + ].filter(Boolean); + + for (const c of candidates) { + const key = normalizeExcelKey(c); + const match = idx.get(key); + if (match && match.rows.length) return match; + } + return null; +} + +function findExcelRowsForProduct(product) { + const match = findExcelForProduct(product); + return match?.rows || []; +} + +function guessColumnKey(row, patterns) { + const keys = Object.keys(row || {}); + for (const re of patterns) { + const k = keys.find(x => re.test(String(x))); + if (k) return k; + } + return null; +} + +function normalizeValue(value) { + return String(value || '') + .replace(/<[^>]*>/g, '') + .replace(/\s+/g, ' ') + .trim(); +} + +function getUniqueNonEmpty(options) { + const uniq = []; + const seen = new Set(); + for (const v of options.map(normalizeValue).filter(Boolean)) { + const k = v.toLowerCase(); + if (seen.has(k)) continue; + seen.add(k); + uniq.push(v); + } + return uniq; +} + +function looksNumeric(value) { + const v = normalizeValue(value).replace(/,/g, '.'); + return /^-?\d+(?:\.\d+)?$/.test(v); +} + +// Test the enrichment for a specific product +const products = JSON.parse(fs.readFileSync('data/processed/products.json', 'utf8')); +const testProduct = products.find(p => p.slug === 'na2xsfl2y-3'); + +if (testProduct) { + console.log('=== Original Product ==='); + console.log('ID:', testProduct.id); + console.log('Slug:', testProduct.slug); + console.log('Name:', testProduct.name); + console.log('Attributes:', testProduct.attributes?.length || 0); + + const rows = findExcelRowsForProduct(testProduct); + console.log('\n=== Excel Rows Found ==='); + console.log('Rows:', rows.length); + + if (rows.length > 0) { + console.log('\nFirst row columns:', Object.keys(rows[0])); + console.log('\nFirst row sample:', JSON.stringify(rows[0], null, 2).substring(0, 500)); + + // Test cross-section detection + const csKey = guessColumnKey(rows[0], [ + /number of cores and cross-section/i, + /cross.?section/i, + /ross section conductor/i, + ]); + console.log('\nCross-section key:', csKey); + + if (csKey) { + const cfgOptions = rows + .map(r => normalizeValue(String(r?.[csKey] ?? ''))) + .filter(Boolean); + console.log('Configurations found:', cfgOptions.length); + console.log('Sample configs:', cfgOptions.slice(0, 5)); + } + + // Test additional columns + const conductorKey = guessColumnKey(rows[0], [/conductor/i]); + const insulationKey = guessColumnKey(rows[0], [/insulation/i]); + const sheathKey = guessColumnKey(rows[0], [/sheath/i]); + const normKey = guessColumnKey(rows[0], [/norm|standard|iec|vde/i]); + + console.log('\nAdditional column keys:'); + console.log(' Conductor:', conductorKey); + console.log(' Insulation:', insulationKey); + console.log(' Sheath:', sheathKey); + console.log(' Norm:', normKey); + + if (conductorKey) { + const values = getUniqueNonEmpty(rows.map(r => normalizeValue(String(r?.[conductorKey] ?? '')))); + console.log('\nConductor values:', values); + } + } +} \ No newline at end of file diff --git a/test-excel.js b/test-excel.js new file mode 100644 index 00000000..ce21588e --- /dev/null +++ b/test-excel.js @@ -0,0 +1,101 @@ +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +const EXCEL_SOURCE_FILES = [ + path.join(process.cwd(), 'data/source/high-voltage.xlsx'), + path.join(process.cwd(), 'data/source/medium-voltage-KM.xlsx'), + path.join(process.cwd(), 'data/source/low-voltage-KM.xlsx'), + path.join(process.cwd(), 'data/source/solar-cables.xlsx'), +]; + +function normalizeExcelKey(value) { + return String(value || '') + .toUpperCase() + .replace(/-\d+$/g, '') + .replace(/[^A-Z0-9]+/g, ''); +} + +function loadExcelRows(filePath) { + const out = execSync(`npx -y xlsx-cli -j "${filePath}"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }); + const trimmed = out.trim(); + const jsonStart = trimmed.indexOf('['); + if (jsonStart < 0) return []; + const jsonText = trimmed.slice(jsonStart); + try { + return JSON.parse(jsonText); + } catch { + return []; + } +} + +function getExcelIndex() { + const idx = new Map(); + for (const file of EXCEL_SOURCE_FILES) { + if (!fs.existsSync(file)) continue; + const rows = loadExcelRows(file); + console.log(`[excel] loaded ${rows.length} rows from ${path.relative(process.cwd(), file)}`); + + const unitsRow = rows.find(r => r && r['Part Number'] === 'Units') || null; + const units = {}; + if (unitsRow) { + for (const [k, v] of Object.entries(unitsRow)) { + if (k === 'Part Number') continue; + const unit = String(v ?? '').trim(); + if (unit) units[k] = unit; + } + } + + for (const r of rows) { + const pn = r?.['Part Number']; + if (!pn || pn === 'Units') continue; + const key = normalizeExcelKey(String(pn)); + if (!key) continue; + const cur = idx.get(key); + if (!cur) { + idx.set(key, { rows: [r], units }); + } else { + cur.rows.push(r); + if (Object.keys(cur.units).length < Object.keys(units).length) cur.units = units; + } + } + } + return idx; +} + +const idx = getExcelIndex(); +console.log('\n=== Excel Index Keys ==='); +for (const [key, match] of idx.entries()) { + console.log(`Key: ${key} | Rows: ${match.rows.length}`); + if (match.rows.length > 0) { + console.log(' Sample columns:', Object.keys(match.rows[0]).slice(0, 10).join(', ')); + } +} + +// Test specific products +const products = JSON.parse(fs.readFileSync('data/processed/products.json', 'utf8')); +const testProducts = ['na2xsfl2y-3', 'h1z2z2-k', 'na2xsfl2y', 'h1z2z2k']; + +console.log('\n=== Product Lookup Tests ==='); +testProducts.forEach(slug => { + const product = products.find(p => p.slug === slug); + if (product) { + const candidates = [ + product.name, + product.slug ? product.slug.replace(/-\d+$/g, '') : '', + product.sku, + product.translationKey, + ].filter(Boolean); + + const keys = candidates.map(c => normalizeExcelKey(c)); + const matches = keys.map(k => idx.get(k)).filter(Boolean); + + console.log(`\nProduct: ${slug} (${product.name})`); + console.log(` Candidates: ${candidates.join(', ')}`); + console.log(` Keys: ${keys.join(', ')}`); + console.log(` Excel matches: ${matches.length}`); + if (matches.length > 0) { + console.log(` Rows found: ${matches[0].rows.length}`); + } + } +}); \ No newline at end of file