// /scripts/convert-professors.js 'use strict'; const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const ODS_FILE = path.resolve(__dirname, '../../raw-data/Untitled spreadsheet.ods'); const OUTPUT_FILE = path.resolve(__dirname, '../../raw-data/professors-import.json'); const pythonExtractScript = ` import zipfile, xml.etree.ElementTree as ET, json, sys ods_path = sys.argv[1] with zipfile.ZipFile(ods_path) as z: content = z.read('content.xml') tree = ET.fromstring(content) for table in tree.iter('{urn:oasis:names:tc:opendocument:xmlns:table:1.0}table'): rows = [] for row in table.iter('{urn:oasis:names:tc:opendocument:xmlns:table:1.0}table-row'): row_vals = [] for cell in row.iter('{urn:oasis:names:tc:opendocument:xmlns:table:1.0}table-cell'): texts = [t.text for t in cell.iter('{urn:oasis:names:tc:opendocument:xmlns:text:1.0}p') if t.text] repeat = int(cell.attrib.get('{urn:oasis:names:tc:opendocument:xmlns:table:1.0}number-columns-repeated', 1)) val = ' '.join(texts).strip() if texts else '' if repeat > 1 and not val: pass else: for _ in range(min(repeat, 20)): row_vals.append(val) if any(row_vals): rows.append(row_vals) print(json.dumps(rows, ensure_ascii=False)) break `; const extractOdsRows = (filePath) => { const output = execSync(`python3 -c "${pythonExtractScript.replace(/"/g, '\\"')}" "${filePath}"`, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 }); return JSON.parse(output); }; const toEnglishDigits = (str = '') => String(str) .replace(/[۰-۹]/g, (d) => '0123456789'['۰۱۲۳۴۵۶۷۸۹'.indexOf(d)]) .replace(/[٠-٩]/g, (d) => '0123456789'['٠١٢٣٤٥٦٧٨٩'.indexOf(d)]); const cleanPhone = (raw) => { if (!raw) return ''; let digits = toEnglishDigits(raw).replace(/\D/g, ''); if (digits.startsWith('98') && digits.length >= 12) digits = `0${digits.slice(2)}`; if (digits.length === 10 && digits.startsWith('9')) digits = `0${digits}`; if (digits.length === 11 && digits.startsWith('9')) digits = `0${digits.slice(0, 10)}`; if (digits.length > 11 && digits.startsWith('09')) digits = digits.slice(0, 11); return digits; }; const cleanNationalId = (raw) => { if (!raw) return ''; let digits = toEnglishDigits(raw).replace(/\D/g, ''); if (digits && digits.length < 10 && /^\d+$/.test(digits)) { digits = digits.padStart(10, '0'); } return digits; }; const cleanCard = (raw) => { if (!raw) return ''; return toEnglishDigits(raw).replace(/\D/g, ''); }; const cleanShaba = (raw) => { if (!raw) return ''; let val = toEnglishDigits(raw).replace(/[^0-9A-Za-z]/g, ''); return val; }; const main = () => { if (!fs.existsSync(ODS_FILE)) { console.error(`ODS file not found: ${ODS_FILE}`); process.exit(1); } const rows = extractOdsRows(ODS_FILE); if (!rows || rows.length < 2) { console.error('No rows found in ODS file'); process.exit(1); } const header = rows[0]; console.log('Detected headers:', header); const professors = []; for (let i = 1; i < rows.length; i += 1) { const r = rows[i]; // 0: ردیف, 1: نام, 2: نام خانوادگی, 3: کد ملی, 4: شماره تماس, 5: درصد, 6: شماره کارت, 7: شمار شبا const rawName = r[1] || ''; const rawSurname = r[2] || ''; const rawNid = r[3] || ''; const rawPhone = r[4] || ''; const rawCard = r[6] || ''; const rawShaba = r[7] || ''; // Strip honorifics like 'سرکار' const name = rawName.replace(/^(آقای|اقای|خانم|آقا|سرکار\s+خانم|سرکار)\s+/, '').trim(); const surname = rawSurname.trim(); const nationalIdCode = cleanNationalId(rawNid); const phoneNumber = cleanPhone(rawPhone); const cardNumber = cleanCard(rawCard); const shabaNumber = cleanShaba(rawShaba); professors.push({ name, surname, nationalIdCode, phoneNumber, cardNumber, shabaNumber }); } const payload = { version: 1, generatedAt: new Date().toISOString(), source: 'Untitled spreadsheet.ods', summary: { professors: professors.length }, professors }; fs.writeFileSync(OUTPUT_FILE, JSON.stringify(payload, null, 2), 'utf-8'); console.log(`Successfully wrote ${professors.length} professors to ${OUTPUT_FILE}`); }; if (require.main === module) { main(); } module.exports = { extractOdsRows, main };