Module:Exchangerate
Appearance
Module status
[edit]The module is currently only able to convert euro in currencies available at c:Data:ECB euro foreign exchange reference rates.tab and c:Data:Xe.com exchange rates.tab.
Functionality
[edit]The function rate
returns the number of 1 unit of the currency given as
"source" in the currency given as "target" as the raw value
calculated from available numbers.
If "verbose" is set, the module throws an error
if no conversion rate is available.
The function revisionTime
takes "source", "target" and "verbose" and returns
the date of the last update for the rate.
The function convert
takes "source", "target" and "verbose" and additionally
"amount". It converts the number given in "amount" in the "source" currency
into the "target" currency with rounded digits.
local errormsg = ('[[Category:Articles that use unexpected currency]]'
.. '<span class="exchangeinfo" style="display:none;" '
.. 'title="Exchange rate not found">Unexpected currency</span>'
.. '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 = '<abbr title="%s%s">%s</abbr>'
local result = string.format(resultFormat, currencyWithUniqueSymbol,
conversions, currencyWithShortSymbol)
return result
end
return p