UNPKG

purchasing-power-parity-advanced

Version:

An advanced PPP conversion calculator with optional flags and country names, and country code listing functions.

157 lines (147 loc) 6.18 kB
#!/usr/bin/env node const fetch = require('node-fetch').default; const { parse } = require('csv-parse/sync'); const { countries } = require('country-data'); // build a lookup map from ISO3 to country metadata const countryMap = {}; if (countries.all && Array.isArray(countries.all)) { countries.all.forEach(c => { if (c.alpha3) countryMap[c.alpha3] = c; }); } const emojiFlags = require('emoji-flags'); const currencyCodes = require('currency-codes'); const getSymbolFromCurrency = require('currency-symbol-map'); // Async loader for PPP data from remote CSV const PPP_CSV_URL = 'https://raw.githubusercontent.com/datasets/ppp/main/data/ppp-gdp.csv'; let cachedPPP = null; async function loadPPPData() { if (cachedPPP) return cachedPPP; const res = await fetch(PPP_CSV_URL); if (!res.ok) throw new Error(`Failed to fetch PPP CSV: ${res.statusText}`); const text = await res.text(); const records = parse(text, { columns: true, skip_empty_lines: true }); // build a map from alpha2 to alpha3 for country codes const alpha2To3 = {}; countries.all.forEach(c => { if (c.alpha2 && c.alpha3) alpha2To3[c.alpha2] = c.alpha3; }); const map = {}; for (const rec of records) { const code2 = rec['Country ID']; const code = alpha2To3[code2]; const year = parseInt(rec['Year'], 10); const pppVal = parseFloat(rec['PPP']); if (!code || isNaN(pppVal)) continue; if (!map[code] || year > map[code].date) { map[code] = { date: year, ppp: pppVal }; } } cachedPPP = map; return map; } /** * Convert an amount from origin to target countries using PPP factors. * @param {string} originCode - ISO3 code of the origin country. * @param {number} amount - Amount in the origin country's currency. * @param {string[]} targetCodes - Array of ISO3 codes for target countries. * @param {object} [options] - Optional settings. * @param {boolean} [options.includeFlags=true] - Whether to include country flag emojis. * @param {boolean} [options.includeNames=true] - Whether to include full country names. * @returns {Promise<{origin:object, results:object[]}>} */ async function convertPPP(originCode, amount, targetCodes, options = {}) { const { includeFlags = true, includeNames = true } = options; const latestPPP = await loadPPPData(); const origin = latestPPP[originCode]; if (!origin) { throw new Error(`PPP data not available for origin country: ${originCode}`); } // gather origin metadata const originCountry = countryMap[originCode] || {}; const originAlpha2 = originCountry.alpha2; const originFlag = includeFlags && originAlpha2 && emojiFlags.countryCode(originAlpha2) ? emojiFlags.countryCode(originAlpha2).emoji : null; const originCurrencyCode = originCountry.currencies ? originCountry.currencies[0] : null; const originCurrencyInfo = originCurrencyCode && currencyCodes.code(originCurrencyCode); const originCurrencyName = originCurrencyInfo ? originCurrencyInfo.currency : null; const originCurrencySymbol = originCurrencyCode ? getSymbolFromCurrency(originCurrencyCode) : null; const originFullName = includeNames ? originCountry.name : undefined; const results = targetCodes.map(code => { const target = latestPPP[code]; if (!target) { return { code, amount: null, flag: includeFlags ? null : undefined, currencyCode: null, currencyName: null, currencySymbol: null, countryName: includeNames ? null : undefined }; } const intlDollars = amount / origin.ppp; const converted = intlDollars * target.ppp; // gather target metadata const targetCountry = countryMap[code] || {}; const targetAlpha2 = targetCountry.alpha2; const targetFlag = includeFlags && targetAlpha2 && emojiFlags.countryCode(targetAlpha2) ? emojiFlags.countryCode(targetAlpha2).emoji : null; const targetCurrencyCode = targetCountry.currencies ? targetCountry.currencies[0] : null; const targetCurrencyInfo = targetCurrencyCode && currencyCodes.code(targetCurrencyCode); const targetCurrencyName = targetCurrencyInfo ? targetCurrencyInfo.currency : null; const targetCurrencySymbol = targetCurrencyCode ? getSymbolFromCurrency(targetCurrencyCode) : null; const targetFullName = includeNames ? targetCountry.name : undefined; return { code, amount: converted, flag: targetFlag, currencyCode: targetCurrencyCode, currencyName: targetCurrencyName, currencySymbol: targetCurrencySymbol, countryName: targetFullName }; }); return { origin: { code: originCode, amount, flag: originFlag, currencyCode: originCurrencyCode, currencyName: originCurrencyName, currencySymbol: originCurrencySymbol, countryName: originFullName }, results }; } /** * List all available country ISO3 codes in the PPP dataset. * @returns {string[]} Sorted array of ISO3 country codes */ async function listCountries() { const latestPPP = await loadPPPData(); return Object.keys(latestPPP).sort(); } /** * List all countries with ISO3 and ISO2 codes and full country names. * @returns {object[]} Array of objects: { iso3, iso2, name } */ function listCountryCodes() { return Object.values(countryMap) .map(c => ({ iso3: c.alpha3, iso2: c.alpha2, name: c.name })) .sort((a, b) => a.name.localeCompare(b.name)); } // CLI interface (async) if (require.main === module) { (async () => { const args = process.argv.slice(2); if (args.length < 3) { console.error('Usage: ppp-calculator <ORIG_CODE> <AMOUNT> <TARGET1,TARGET2,...>'); process.exit(1); } const [orig, amtStr, targets] = args; const amount = parseFloat(amtStr); const targetCodes = targets.split(',').map(s => s.trim().toUpperCase()); try { const result = await convertPPP(orig.toUpperCase(), amount, targetCodes); console.log(JSON.stringify(result, null, 2)); } catch (err) { console.error(err.message); process.exit(1); } })(); } module.exports = { convertPPP, listCountries, listCountryCodes };