Modulo:GetNumber
La documentazione per questo modulo può essere creata in Modulo:GetNumber/man
--[=[ GetNumber 2023-12-31
* local nilIf (nil handling function for internal use)
* local coalesce (nil handling function for internal use)
* local _round (rounding function for internal use)
*
* local getNumbersAsTable (internal use)
*
* getNumbersWithUnit
* getNumbersWithDate
]=]
local GetNumber = {}
-- it: format 1234.56 this way: 1 234,56 (as per international syntax)
-- de: format 1234.56 this way: 1.234,56 (as per Italian & German syntax)
local lang = mw.language.new( 'de' )
-- units and conversion
-- getting conversion data from wikidata is not implemented yet
local units = require ( 'Module:GetNumber/Units' )
-- returns nil, if both values are equal, otherwise the value
-- similar to the SQL function nullif()
local function nilIf ( value, equalValue )
if ( value == nil ) then
return nil
elseif ( tostring ( value ) == tostring ( equalValue ) ) then
return nil
else
return value
end
end
-- returns the first value that is not nil
-- similar to the SQL function coalesce()
local function coalesce ( value1, value2, value3 )
return value1 or value2 or value3
end
-- round function, which is not available in Lua
local _round = function ( value, precision )
local rescale = math.pow(10, precision or 0);
return math.floor(value * rescale + 0.5) / rescale;
end
-- getNumbersAsTable() -> just for internal use.
-- gets the amount of a property including its unit and date of validity
-- id: Wikidata-ID (own, if not provided)
-- property: requested property
-- unit: wanted unit (standard unit, if not provided)
-- if the wanted unit is not provided, a conversion will be tried based on the given conversion numbers
-- values:
-- - single (standard): Only one (first) entry is shown
-- - all: All entries are fetched
-- the functions returns a table with the folowing columns:
-- amount
-- unit
-- unitOriginal (differs to unit, if the requested unit is converted)
-- year (set to 0, if not provided)
local getNumbersAsTable = function ( id, property, unit, values )
-- local variables
-- ID of the item
-- Determined, if not provided
local localID = id or mw.wikibase.getEntityIdForCurrentPage() or ''
-- compatibility to existing modules and templates:
-- some use the keyword "self" for using the own entity-ID
if coalesce ( nilIf ( localID, 'self' ), '' ) == '' then
localID = mw.wikibase.getEntityIdForCurrentPage() or ''
end
-- no Wikidata object
if localID == '' then
return {}, '', ''
end
-- property
local requestedProperty = property or 'none';
if string.sub(requestedProperty,1,1) ~= 'P' then
requestedProperty = 'none'
end
-- no property given: exit
if requestedProperty == 'none' then
return {}, localID
end
-- property unknown in table "units": exit
if units[requestedProperty] == nil then
return {}, localID
end
-- unit
local requestedUnit = 'none'
if coalesce ( unit, '' ) == '' then
if units[requestedProperty] ~= nil then
requestedUnit = units[requestedProperty].standard
end
else
requestedUnit = unit
if units[requestedUnit] == nil then
end
end
-- values
local requestedValues = values or 'single'
-- control variable
-- is set to true, when requested unit is got on the first run
local hasRequested = false
-- getting the values
local wdStatements = mw.wikibase.getBestStatements( localID, requestedProperty )
-- running through the array and store it in a simple table
local wdValues = {}
local referenceValues = {}
local entryValue
for i, entry in ipairs ( wdStatements ) do
-- check for value
if entry.mainsnak.snaktype == 'value' then
-- check for number
if entry.mainsnak.datatype == 'quantity' then
-- new data set
entryValue = {}
-- getting the amount, converted into a number
entryValue.amount = tonumber ( entry.mainsnak.datavalue.value.amount )
-- getting the unit
entryValue.unit = entry.mainsnak.datavalue.value.unit:gsub( 'https?://www.wikidata.org/entity/', '' ) or ''
-- save a copy of the original unit
-- in case somebody wants to know, whether a value is converted
entryValue.unitOriginal = entryValue.unit
-- check, whether its the requested unit
if entryValue.unit == requestedUnit then
hasRequested = true
end
-- initialising qualifiers
entryValue.year = 0
-- read qualifiers
if entry.qualifiers ~= nil then
-- read P585-Qualifiers (date/time of validity)
if entry.qualifiers.P585 ~= nil then
if entry.qualifiers.P585[1].datavalue.value.precision == 9 then
entryValue.year = tonumber ( entry.qualifiers.P585[1].datavalue.value.time:sub( 2, 5 ) )
entryValue.time = entry.qualifiers.P585[1].datavalue.value.time
end
if entry.qualifiers.P585[1].datavalue.value.precision == 10 then
entryValue.year = tonumber ( entry.qualifiers.P585[1].datavalue.value.time:sub( 2, 5 ) )
entryValue.time = entry.qualifiers.P585[1].datavalue.value.time
end
if entry.qualifiers.P585[1].datavalue.value.precision == 11 then
entryValue.year = tonumber ( entry.qualifiers.P585[1].datavalue.value.time:sub( 2, 5 ) )
entryValue.time = entry.qualifiers.P585[1].datavalue.value.time
end
end
-- read P2144-Qualifier (frequency)
if entry.qualifiers.P2144 ~= nil then
if entry.qualifiers.P2144[1].datavalue ~= nil then
entryValue.P2144 = tonumber ( entry.qualifiers.P2144[1].datavalue.value.amount )
end
end
end
referenceValues = {}
-- read references
if entry.references ~= nil then
for j, referenceEntry in ipairs ( entry.references ) do
if referenceEntry.snaks ~= nil then
if referenceEntry.snaks.P854 ~= nil then
if referenceEntry.snaks.P854[1].snaktype == 'value' then
table.insert( referenceValues, referenceEntry.snaks.P854[1].datavalue.value )
end
end
end
end -- for j, referenceEntry in ipairs ( entry.references )
end -- if entry.references ~= nil
entryValue.references = referenceValues
-- adding to the list
table.insert( wdValues, entryValue )
end -- if entry.mainsnak.datatype == 'quantity'
end -- if entry.mainsnak.snaktype == 'value'
end -- for i, entry in ipairs ( wdStatements )
-- nothing found then exit
if wdValues == {} then
return {}, localID
end
-- generating the returning values
local returnValues = {}
-- controll tag, for getting all or just one value
local noFetch = false
-- iterating variable
local i = 1
-- requested unit found?
if hasRequested then
-- getting the values, no conversion needed
repeat
-- getting a row
entryValue = wdValues[i]
-- put the entry to the list, if its the requested unit
if entryValue.unit == requestedUnit then
-- adding the row to the final result table
table.insert( returnValues, entryValue )
-- stopping, if only one value is requested
if requestedValues == 'single' then
noFetch = true
end
end
-- counting up
i = i + 1
until noFetch or i > #wdValues
-- requested unit not found
-- trying conversion
else
-- tagging, if its succeeded at least once
local conversionSuccess = true
-- check, whether there is at least on data set
if wdValues[i] ~= nil then
-- initialize with false
conversionSuccess = false
-- getting the values and trying conversion
repeat
-- getting a row
entryValue = wdValues[i]
-- put the entry to the list, if the unit and conversion are available
if units[entryValue.unit] ~= nil then
if units[entryValue.unit].conversion[requestedUnit] ~= nil then
-- adding the row to the final result table
table.insert(
returnValues,
{
amount = entryValue.amount * units[entryValue.unit].conversion[requestedUnit],
unit = requestedUnit,
unitOriginal = entryValue.unit,
year = entryValue.year
}
)
-- stopping, if only one value is requested
if requestedValues == 'single' then
noFetch = true
end
-- tagging as success
conversionSuccess = true
end
end
-- counting up
i = i + 1
until noFetch or i > #wdValues
end
end -- if hasRequested then
return returnValues, localID
end
-- getNumbersWithUnit()
-- gets the amount of a property including its unit
-- first parameters: see internal function
-- values:
-- - single (standard): Only one (first) entry is shown
-- - all: All entries are fetched
-- precision: precision of the number (Standard: 0)
-- show:
-- - number: formatted amount without unit
-- - plain: not formatted amount without unit
-- - short (standard): amount with short unit
-- - long: amount with long unit
-- - short-date: short with date
-- - short-date-ref: short with date and reference(s)
-- delimiter: delimiter between the numbers; standard is comma and breakable space
GetNumber.getNumbersWithUnit = function ( id, property, unit, values, precision, show, delimiter, frame )
-- returning String
local numberString = ''
-- making empty parameters to nil
if unit == '' then unit = nil end
if values == '' then values = nil end
-- WD-Values
local numberList
local wikidataID
numberList, wikidataID = getNumbersAsTable ( id, property, unit, values )
-- precision
local numberPrecision = tonumber ( precision ) or 0
-- show options
local numberShow = show or 'short'
-- no delimiter in front of the first entry
local numberDelimiter = ''
if numberList[1] == nil then
return nil
end
-- displaying the values
for i, entry in ipairs( numberList ) do
-- no unit given or wanted
if numberShow == 'number' then
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) )
elseif numberShow == 'plain' then
numberString = numberString .. numberDelimiter .. tostring ( entry.amount, precision )
-- displaying with unit using its long name
elseif numberShow == 'long' then
if units[entry.unit] ~= nil then
if _round ( entry.amount, precision ) == 1 then
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) ) .. ' ' .. units[entry.unit].longSingle
else
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) ) .. ' ' .. units[entry.unit].long
end
else
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) )
end
-- displaying with unit using its abbreviation
elseif numberShow == 'short-date' then
if units[entry.unit] ~= nil then
if entry.time ~= nil then
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) ) .. ' ' .. units[entry.unit].short .. ' <small>(' .. lang:formatDate( 'd.m.Y', entry.time ) .. ')</small>'
else
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) ) .. ' ' .. units[entry.unit].short
end
else
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) )
end
-- displaying with unit using its abbreviation
elseif numberShow == 'short-date-ref' then
if units[entry.unit] ~= nil then
if entry.time ~= nil then
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) ) .. ' ' .. units[entry.unit].short .. ' <small>(' .. lang:formatDate( 'd.m.Y', entry.time ) .. ')</small>'
else
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) ) .. ' ' .. units[entry.unit].short
end
else
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) )
end
if #entry.references > 0 then
for j, refEntry in ipairs ( entry.references ) do
numberString = numberString .. frame:extensionTag{ name = 'ref', content = '[' .. refEntry .. ' ' .. refEntry .. ']' }
end
end
-- displaying with unit using its abbreviation
else
if units[entry.unit] ~=nil then
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) ) .. ' ' .. units[entry.unit].short
else
numberString = numberString .. numberDelimiter .. lang:formatNum ( _round ( entry.amount, precision ) )
end
end
-- adding the frequency qualifikator, in case of mains voltage is requested
if property == 'P2884' and entry.P2144 ~= nil then
numberString = numberString .. ', ' .. entry.P2144 .. ' Hz'
end
-- setting the delimiter after the first entry
numberDelimiter = delimiter or ', '
end
-- providing the list of numbers
return numberString
end
-- getNumbersWithDate()
-- gets the amount of a property including "valid at" date
-- first parameters: see internal function
-- values:
-- - single (standard): Only one (first) entry is shown
-- - all: All entries are fetched
-- show:
-- - number: Only the formatted number is shown (Standard)
-- - plain: Only the Not formatted number is shown
-- - year: the year of validity is shown
-- delimiter: delimiter between the numbers; standard is comma and breakable space
GetNumber.getNumbersWithDate = function ( id, property, values, show, delimiter )
-- returning String
local numberString = ''
-- making empty parameters to nil
if values == '' then values = nil end
if show == '' then show = nil end
-- setting to defaul, if necessary
values = values or 'single'
show = show or 'number'
-- WD-Values
local numberList
local wikidataID
-- always fetching all to be sorted later, because "singles" normally means the "most recent"
numberList, wikidataID = getNumbersAsTable ( id, property, nil, 'all' )
if numberList[1] == nil then
return nil
end
--- Sorting the table by year
table.sort(numberList, function(a,b) return a.year < b.year end)
-- no delimiter in front of the first entry
local numberDelimiter = ''
-- displaying the values
if values == 'single' then
-- using the first table row
if show == 'year' then
numberString = lang:formatNum ( numberList[1].amount ) .. ' (' .. numberList[1].year .. ')'
elseif show == 'plain' then
numberString = numberList[1].amount
else
numberString = lang:formatNum ( numberList[1].amount )
end
else
-- running through the table
for i, entry in ipairs( numberList ) do
-- adding an entry
if show == 'year' then
numberString = numberString .. numberDelimiter .. lang:formatNum ( entry.amount ) .. ' <span class="voy-small">(' .. entry.year .. ')</span>'
elseif show == 'plain' then
numberString = numberString .. numberDelimiter .. tostring ( entry.amount )
else
numberString = numberString .. numberDelimiter .. lang:formatNum ( entry.amount )
end
-- setting the delimiter after the first entry
numberDelimiter = delimiter or ', '
end
end
-- providing the list of numbers
return numberString
end
-- Providing template access
local p = {}
function p.getNumbersWithUnit( frame )
return GetNumber.getNumbersWithUnit( frame.args[ 1 ], frame.args[ 2 ], frame.args[ 3 ], frame.args[ 4 ], frame.args[ 5 ], frame.args.show or frame.args[ 6 ], frame.args.delimiter or frame.args[ 7 ], frame ) or ""
end
function p.getNumbersWithDate( frame )
return GetNumber.getNumbersWithDate( frame.args[ 1 ], frame.args[ 2 ], frame.args[ 3 ], frame.args.show or frame.args[ 4 ], frame.args.delimiter or frame.args[ 5 ] ) or ""
end
-- for usage in other modules
-- using it the following way:
--
-- local getNumber = require( 'Module:GetNumber' )
-- foo = getNumber.GetNumber().xxx( id, lang )
function p.GetNumber()
return GetNumber
end
return p