Module:Exchangerate

local errormsg = (''	.. 'Unexpected currency '	.. 'rate not found')

local function countSigificantDigits(number) number = string.gsub(number, '%.', '', 1) number = mw.text.trim(number, '0') return #number end

local function round(num, numSigificantDigits) local numDecimalPlaces = numSigificantDigits - math.floor(math.log10(num)) - 1 local mult = 10^(numDecimalPlaces or 0) return math.floor(num * mult + 0.5) / mult end

local function getTabularDataFieldNames(tabularData) local fields = {} for _,field in pairs(tabularData.schema.fields) do		table.insert(fields, field.name) end return fields end

local function getColumnIndices(fields) local rowCurrencyIndex, dateIndex local targetCurrencyIndices = {} local sourceCurrencyIndices = {} for i,v in pairs(fields) do		if v == 'currency' then rowCurrencyIndex = i		elseif v == 'date' then dateIndex = i		elseif string.match(v, '^%u%u%u$') then sourceCurrencyIndices[v] = i		elseif string.match(v, '^_%u%u%u$') then targetCurrencyIndices[string.sub(v,2)] = i		end end return rowCurrencyIndex, dateIndex, sourceCurrencyIndices, targetCurrencyIndices end

local function getConversionTable(dataPageName) local tabularData = mw.ext.data.get(dataPageName) if not tabularData then return nil end local fields = getTabularDataFieldNames(tabularData) local rowCurrencyIndex, dateIndex, sourceCurrencyIndices, targetCurrencyIndices = getColumnIndices(fields) local conversionTable = {} if rowCurrencyIndex then for _,row in pairs(tabularData.data) do			for sourceCurrency,index in pairs(sourceCurrencyIndices) do				if not conversionTable[sourceCurrency] then conversionTable[sourceCurrency] = {} end conversionTable[sourceCurrency][row[rowCurrencyIndex]] = {rate = row[index], revisionTime = row[dateIndex]} end for targetCurrency,index in pairs(targetCurrencyIndices) do				if not conversionTable[row[rowCurrencyIndex]] then conversionTable[row[rowCurrencyIndex]] = {} end conversionTable[row[rowCurrencyIndex]][targetCurrency] = {rate = row[index], revisionTime = row[dateIndex]} end end end return conversionTable end

local function getDataFromRateDataPage(dataPageName, source, target) local conversionTable = getConversionTable(dataPageName) if not conversionTable then return nil end local rate, revisionTime if conversionTable[source] and conversionTable[source][target] then rate = conversionTable[source][target]['rate'] rateSignificantDigits = countSigificantDigits(rate) revisionTime = conversionTable[source][target]['revisionTime'] elseif conversionTable[target] and conversionTable[target][source] then local targetToSourceRate = conversionTable[target][source]['rate'] rate = targetToSourceRate^-1 rateSignificantDigits = countSigificantDigits(targetToSourceRate) revisionTime = conversionTable[target][source]['revisionTime'] end return rate, rateSignificantDigits, revisionTime end

local p = {}

function p._rate(source, target, rounded) local dataPageNames = { 'ECB euro foreign exchange reference rates.tab', 'Xe.com exchange rates.tab'} local rate, revisionTime, rateSignificantDigits for _,name in pairs(dataPageNames) do		rate, rateSignificantDigits, revisionTime = getDataFromRateDataPage(name, source, target) if not rate or not revisionTime then for _,name in pairs(dataPageNames) do				local USDtoTargetRate, UtoTSigDig, UtoTRevTime = getDataFromRateDataPage(name, 'USD', target) local USDtoSourceRate, UtoSSigDig, UtoSRevTime = getDataFromRateDataPage(name, 'USD', source) if USDtoTargetRate and USDtoSourceRate then rate = USDtoTargetRate/USDtoSourceRate revisionTime = UtoTRevTime < UtoSRevTime and UtoTRevTime or UtoSRevTime rateSignificantDigits = UtoTSigDig < UtoSSigDig and UtoTSigDig or UtoSSigDig end end end if rate and revisionTime then break end end if rate and revisionTime then if rounded then rate = round(rate, rateSignificantDigits) end return rate, revisionTime end end

function p._convert(source, target, amount) local rate = p._rate(source, target) if rate then local amountSigificantDigitsCount = countSigificantDigits(amount) return round(amount * rate, amountSigificantDigitsCount + 1) end end

function p._convertSingelOrRange(source, target, amounts) local amounts = string.gsub(amounts, ',', '') local splitOffset = mw.ustring.find(amounts, '-') local converted if splitOffset then local firstAmount = mw.ustring.sub(amounts, 0, splitOffset -1) local secondAmount = mw.ustring.sub(amounts, splitOffset + 1) local first = p._convert(source, target, firstAmount) local second = p._convert(source, target, secondAmount) converted = first and second and first .. '–' .. second else converted = p._convert(source, target, amounts) end return converted end

function p.rate(frame) local args = frame.args local rate = p._rate(args.source, args.target, true) local result = rate or args.verbose and errormsg return result end

function p.revisionTime(frame) local args = frame.args local _,revisionTime = p._rate(args.source, args.target) local result = revisionTime or args.verbose and errormsg return result end

function p.convert(frame) local args = frame.args local amount = 	string.gsub(args.amount, ',', '') local convertedAmount = p._convert(args.source, args.target, amount) local result = convertedAmount or args.verbose and errormsg return result end

function p.convertSingelOrRange(frame) local args = frame.args local convertedAmounts = p._convertSingelOrRange(		args.source, args.target, args.amounts) local result = convertedAmounts or args.verbose and errormsg return result end

local function currencyWithSymbol(currency, symbolFormat, amount) local currencyWithSymbol = (		symbolFormat and string.format(symbolFormat, amount)		or currency .. amount) return currencyWithSymbol end

function p.currencyWithConversions(frame) local args = frame.args local amount = (args.amount and args.amount ~= '') and args.amount or 1 local i18n = mw.loadData('Module:Exchangerate/i18n') local currencySymbols = i18n.symbols[args.currency] local shortSymbol = currencySymbols and currencySymbols.shortSymbol local currencyWithShortSymbol = currencyWithSymbol(		args.currency, shortSymbol, amount) local uniqueSymbol = currencySymbols and currencySymbols.uniqueSymbol local currencyWithUniqueSymbol = currencyWithSymbol(		args.currency, uniqueSymbol, amount) local conversionCurrencies = i18n.defaultConversions or {'USD', 'EUR'} local convertedStrings = {} for _,convCurrency in ipairs(conversionCurrencies) do		if args.currency ~= convCurrency then local convertedAmount = p._convertSingelOrRange(				args.currency, convCurrency, amount) local convCurrencyUniqueSymbol = (i18n.symbols[convCurrency]				and i18n.symbols[convCurrency].uniqueSymbol) local convCurrencyWithSymbol = convertedAmount and currencyWithSymbol(				convCurrency, convCurrencyUniqueSymbol, convertedAmount) table.insert(convertedStrings, convCurrencyWithSymbol) end end local comma = mw.message.new('comma-separator'):plain local allConvertedStrings = table.concat(convertedStrings, comma) local conversions = (allConvertedStrings ~= '') and ' ≈ ' .. allConvertedStrings or '' local resultFormat = '%s ' local result = string.format(resultFormat, currencyWithUniqueSymbol,		conversions, currencyWithShortSymbol) return result end

return p