Přeskočit na obsah

Modul:Wikidata/Formatters

Tato stránka je zamčena
Z Wikipedie, otevřené encyklopedie

Interní modul, který zprostředkovává převod strukturovaných dat do wikitextu. Každému typu hodnoty je vytvořen základní formátovací submodul, pro účely specifického formátování lze naprogramovat vlastní formátovací submodul.

Struktura submodulu

-- vzorový submodul, viz [[Modul:Wikidata/Formatters/empty]]

require 'Modul:No globals'

local p = {}

function p.getRawValue(value, options)
	-- extract a human readable value or just call a native module for this type
	return mw.dumpObject(value)
end

function p.formatRawValue(value, options)
	-- do formatting here
	return value
end

function p.formatValue(value, options)
	local raw = p.getRawValue(value, options)
	return p.formatRawValue(raw, options)
end

return p

Doporučeno je implementovat a exportovat tyto tři metody:

getRawValue
Převede strukturovaná data do raw hodnoty, kterou lze dále zpracovávat (např. šablonami nebo parserovými funkcemi ve wikitextu). Návratovou hodnotou může být řetězec nebo objekt (tabulka) implementující metametodu __tostring.
formatRawValue
Převede raw hodnotu (typicky takovou, kterou vrací getRawValue) do wikitextu, který bude zobrazen čtenáři.
formatValue
Zformátuje strukturovaná data do wikitextu, který bude zobrazen čtenáři. Lze implementovat jako ve vzorovém modulu kombinací getRawValue a formatRawValue nebo samostatně, zvlášť pokud by se takto ztrácely informace.

Dostupné submoduly

Základní:

Vlastní:


require "Modul:No globals"

local p = {}

local lib = require 'Modul:Wikidata/lib'

local function formatCoordinateValue(value, typeOfValue, field)
	local function fastConvertDdToDms(ddValue)
		local dmsArr = {
			degrees = 0,
			minutes = 0,
			seconds = 0.0
		}

		if ddValue then
			dmsArr["degrees"] = math.floor(tonumber(ddValue))
			dmsArr["minutes"] = math.floor((tonumber(ddValue) - dmsArr["degrees"]) * 60)
			dmsArr["seconds"] = (tonumber(ddValue) - dmsArr["degrees"] - dmsArr["minutes"]/60) * 3600
		end

		return dmsArr
	end

	-- type bude nepovinny - tj. "nil"
	-- priklad pouze -- je tam asi X chyb (mimo jine N vs S a podobne)
	local result = ""
	local latdmsText, londmsText
	if typeOfValue == 'dms' then
		local latDms = fastConvertDdToDms(value.latitude)
		local lonDms = fastConvertDdToDms(value.longitude)
		latdmsText = mw.ustring.format('N %s° %s\' %s"', latDms["degrees"], latDms["minutes"], latDms["seconds"])
		londmsText = mw.ustring.format('E %s° %s\' %s"', lonDms["degrees"], lonDms["minutes"], lonDms["seconds"])
		if field then
			if field == 'latitude' then
				result = latdmsText
			elseif field == 'longitude' then
				result = londmsText
			end
		else
			result = latdmsText .. " " .. londmsText
		end
	elseif typeOfValue == 'dd' then
		latdmsText = tonumber(value.latitude)
		londmsText = tonumber(value.longitude)
		if field then
			if field == 'latitude' then
				result = latdmsText
			elseif field == 'longitude' then
				result = londmsText
			end
		else
			result = latdmsText .. " " .. londmsText
		end
	else
		result = value.latitude .. ' / ' .. value.longitude .. ' (přesnost: ' .. value.precision .. ')'
	end

	return result
end

local function findPattern(property)
	local entity = mw.wikibase.getEntity(property:upper())
	if entity then
		local Statements = entity:getBestStatements('P1630')
		for _, statement in pairs(Statements) do
			if lib.IsSnakValue(statement.mainsnak) then
				return p.getRawValue(statement.mainsnak)
			end
		end
	end
	return nil
end

local function formatQuantity(value, options)
	local amount = tonumber(value.amount)
	local margin
	if lib.IsOptionTrue(options, 'showmargin') then
		margin = tonumber(value.upperBound) - amount
		if margin == 0 then
			margin = nil
		end
	end
	local prefix
	if amount < 0 then
		amount = tonumber(mw.ustring.sub(amount, 2))
		prefix = '-'
	end
	if lib.IsOptionTrue(options, 'formatted') then
		local function formatNumber(number)
			local integer, decimal
			if mw.ustring.find(number, '%.') then
				integer, decimal = mw.ustring.match(number, '^(.+)%.(.+)$')
			else
				integer = number
			end
			local length = mw.ustring.len(integer)
			local i = length % 3
			if i == 0 then
				i = 3
			end
			local formatted_num = mw.ustring.sub(integer, 1, i)
			while i < length do
				formatted_num = formatted_num .. '&nbsp;' .. mw.ustring.sub(integer, i + 1, i + 3)
				i = i + 3
			end
			if decimal then
				local length = mw.ustring.len(decimal)
				local i = 3
				formatted_num = formatted_num .. ',' .. mw.ustring.sub(decimal, 1, 3)
				while i < length do
					formatted_num = formatted_num .. '&nbsp;' .. mw.ustring.sub(decimal, i + 1, i + 3)
					i = i + 3
				end
			end
			return formatted_num
		end
		amount = formatNumber(amount)
		if margin then
			margin = formatNumber(margin)
		end
	end
	if margin then
		amount = amount .. '±' .. margin
	end
	if prefix then
		amount = prefix .. amount
	end
	local unit = value.unit
	if unit ~= '1' and tostring(options.showunit) ~= 'false' then
		local module = require 'Modul:Wikidata/item'
		unit = mw.ustring.match(unit, '(Q%d+)') -- separate handler
		unit = module.formatEntityId(unit, {})
		amount = amount .. '&nbsp;' .. unit
	end
	return amount
end

local function executeFormatter(formatters, value, options)
	if formatters.module or formatters.withmodule then
		if not formatters.module or not formatters.func then
			return formatError('unknown-value-module')
		end

		local formatter = require ('Modul:' .. formatters.module)
		if not formatter then
			return formatError('value-module-not-found', formatters.module)
		end

		local fun = formatter[formatters.func]
		if not fun then
			return formatError('value-function-not-found', formatters.func)
		end

		return fun(value, options)
	else
		return formatters.func(value, options)
	end
end

local function getFormatter(datavalue, aspect, options)
	local FormattersMap = {
		globecoordinate = {
			formatted = {
				func = function(value, options)
					if not options.field or options.field == '' then
					--	return error(lib.formatError('param-not-provided', 'field'))
						return formatCoordinateValue(datavalue, 'dms')
					elseif options.field == "latitude" or options.field == "longitude" then
						return formatCoordinateValue(datavalue, options.typeOfCoordinates, options.field)
					elseif options.field == "precision" or options.field == "globe" then
						return datavalue.value[options.field]
					else
						return error(lib.formatError('invalid-field', options.field))
					end
				end
			}
		},
		monolingualtext = {
			formatted = {
				func = function(value)
					return '<span lang="' .. value.language .. '">' .. value.text .. '</span>'
				end
			},
			raw = {
				func = function(value)
					return value.text
				end
			}
		},
		quantity = {
			formatted = {
				func = formatQuantity
			},
			raw = {
				func = function(value)
					return tonumber(value.amount)
				end
			}
		},
		string = {
			formatted = {
				func = function(value, options)
					if options.pattern and options.pattern ~= '' then
						return lib.formatFromPattern(value, options.pattern)
					elseif lib.IsOptionTrue(options, 'autoformat') then
						local pattern = findPattern(options.property)
						if pattern then
							return lib.formatFromPattern(value, '[' .. pattern .. ' $1]')
						end
					else
						return value
					end
				end,
			}
		},
		time = {
			formatted = {
				func = 'formatDateFromWikidataValue',
				module = 'Wikidata/datum'
			},
			raw = {
				func = 'newFromWikidataValue',
				module = 'Time'
			}
		},
		["wikibase-entityid"] = {
			convert = {
				func = '',
				module = 'Wikidata/item'
			},
			formatted = {
				func = function(value, options)
					local module = require 'Modul:Wikidata/item'
					return module.formatEntityId(lib.getEntityIdFromValue(value), options)
				end
			},
			raw = {
				func = function(...)
					return lib.getEntityIdFromValue(...)
				end
			}
		},
	}

	if not FormattersMap[datavalue.type] then
		return error(lib.formatError('unknown-datavalue-type', datavalue.type))
	end

	if not FormattersMap[datavalue.type][aspect] then
		if aspect == 'raw' then
			return datavalue.value
		else
			return error() -- TODO
		end
	end

	return executeFormatter(FormattersMap[datavalue.type][aspect], datavalue.value, options)
end

function p.getRawValue(snak, options)
	if snak.snaktype == 'value' then
		return getFormatter(snak.datavalue, 'raw', options)
	elseif snak.snaktype == 'somevalue' or snak.snaktype == 'novalue' then
		return snak.snaktype
	else
		return error(lib.formatError('unknown-snak-type', snak.snaktype))
	end
end

function p.getFormattedValue(snak, options)
	if snak.snaktype == 'somevalue' or snak.snaktype == 'novalue' then
		return snak.snaktype
	elseif snak.snaktype ~= 'value' then
		return error(lib.formatError('unknown-snak-type', snak.snaktype))
	end

	if options['value-module'] or options['value-function'] then
		local formatterMap = {
			func = options['value-function'],
			module = options['value-module'],
			withmodule = true -- force throwing error
		}
		return executeFormatter(formatterMap, snak.datavalue.value, options)
	end

	return getFormatter(snak.datavalue, 'formatted', options)
end

return p