Modul:WikidataIB/testtilfeller

Fra Wikipedia, den frie encyklopedi


local p = {}

local rankOrder = {
	['truth'] = 1,
	['preferred'] = 2,
	['normal'] = 3,
	['deprecated'] = 4
}

local i18n = {
	['wikidata-linkback-edit'] = 'Redigere på Wikidata',
	['wiki-article-not-available'] = 'Artikkelen er ikke tilgjengelig på denne wikien enda',
	['wikidata-more-results-exists'] = 'Flere verdier er tilgjengelige på wikidata',
	['standard-timeformat'] = 'j. F Y',
	['argument-property'] = 'property',
	['argument-list'] = 'liste',
	['argument-date'] = 'dato',
	['argument-value'] = 'verdi',
	['argument-label'] = 'pnavn',
	['argument-noedit'] = 'noedit',
	['argument-fallback'] = 'fallback',
	['argument-suffix'] = 'suffix',
	['argument-suffix-label'] = 'suffixnavn',
	['argument-bounds'] = 'usikkerhet',
	['category-rowproperty-no-arguments'] = '[[Kategori:Artikler med invoke WikidataIB rowProperty uten argumenter]]',
	['category-getpropertyvalue-no-arguments'] = '[[Kategori:Artikler med invoke WikidataIB getPropertyValue uten argumenter]]',
	['category-getpropertylabel-no-arguments'] = '[[Kategori:Artikler med invoke WikidataIB getPropertyLabel uten argumenter]]'
}

local arguments = {
	property = nil,
	list = nil,
	date = nil,
	wiki_value = nil,
	edit = nil,
	fallback = nil,
	usesuffix = nil,
	suffix_from_wiki = nil,
	label = nil,
	usebounds = nil,
}


local function dump(obj)
	return "<pre>" .. mw.dumpObject(obj) .. "</pre>"
end

local function getArgument(frame, argument)
	local args = frame.args
	if args[1] == nil then
		local pFrame = frame:getParent();
		args = pFrame.args;
		for k,v in pairs( frame.args ) do
			args[k] = v;
		end
	 end
	 if args[argument] then
		return args[argument]
	 end
	 return nil
end

local function getArguments(frame)
	arguments.property = getArgument(frame, i18n['argument-property'])
	if not arguments.property then
		-- No such argument, try getting it as the first argument
		arguments.property = getArgument(frame, '1')
	end
	arguments.list = getArgument(frame, i18n['argument-list'])
	if arguments.list and arguments.list ~= "" and tonumber(arguments.list) ~= 0 then
		arguments.list = tonumber(arguments.list)
	else
		arguments.list = nil
	end
	arguments.date = getArgument(frame, i18n['argument-date'])
	arguments.wiki_value = getArgument(frame, i18n['argument-value'])
	arguments.edit = getArgument(frame, i18n['argument-edit'])
	if arguments.edit and arguments.edit == "1" and not arguments.wiki_value then
		arguments.edit = false
	else
		arguments.edit = true
	end
	arguments.fallback = getArgument(frame, i18n['argument-fallback'])
	if arguments.fallback and arguments.fallback ~= "" then
		arguments.fallback = true
	else
		arguments.fallback = false
	end
	arguments.usesuffix = getArgument(frame, i18n['argument-suffix'])
	arguments.suffix_from_wiki = getArgument(frame, i18n['argument-suffix-label'])
	arguments.label = getArgument(frame, i18n['argument-label'])
	arguments.usebounds = getArgument(frame, i18n['argument-bounds'])
end

local function getDate(qualifiers, dateformat, langcode)
	local out = nil
	if qualifiers then
		local qualifierID = ""
		if qualifiers['P585'] then qualifierID='P585'			-- point-in-time
		elseif qualifiers['P580'] then qualifierID='P580' end		-- from
		-- todo: timespans? P582
		for k, v in pairs(qualifiers[qualifierID]) do
			if v.snaktype == 'value' then
				out = "(" .. mw.language.new(langcode):formatDate(dateformat, v.datavalue.value.time) .. ")"
			end
		end
	end
	return out
end

local function getOrder(qualifiers)
	local out = nil
	if qualifiers then
		local qualifierID = "P1545" -- sorting order / rank / series ordinal
		if qualifiers[qualifierID] then
			for k, v in pairs(qualifiers[qualifierID]) do
				if v.snaktype == 'value' then
					out = v.datavalue.value
				end
			end
		end
	end
	return out
end

local function sortTableRankAndOrder(t, order_descending)
	local retval = {}
	local function compare(a, b)
		if (a.rank and b.rank and rankOrder[a.rank] < rankOrder[b.rank]) then
			return true
		end
		if (a.rank and b.rank and rankOrder[a.rank] == rankOrder[b.rank]) and (a.order and b.order and ((order_descending and a.order < b.order) or (not order_descending and a.order > b.order))) then
			return true
		else
			return false
		end
	end
	table.sort(t, compare)
end

local function compareRank(oldrank, newrank)
	if (newrank and not oldrank) or (oldrank and newrank and rankOrder[newrank] < rankOrder[oldrank]) then
		return newrank
	else
		return oldrank
	end
end

local function getQValueLink(qvalue, fallback, fallbacklang, label_from_wiki)
	local sitelink = mw.wikibase.sitelink(qvalue)
	local label = label_from_wiki
	if not label then
		label = mw.wikibase.label(qvalue)
	end
	out = nil
	if qvalue then
		if label == nil then
			-- let's try the fallback-languages
			local checkQ = mw.wikibase.getEntityObject(qvalue)
			if checkQ then
				local i = 0
				while i < #fallbacklang do
					i = i + 1
					label = checkQ:getLabel( fallbacklang[i] )
					if label ~= nil then
						break
					end
				end
					
			end
			if label == nil then
				-- no such luck, fall back to Q-values
				label = qvalue
			end
		end
				
		if sitelink and sitelink ~= label then
			out = "[[" .. sitelink .. "|" .. label .. "]]"
		elseif sitelink  then
			out = "[[" .. sitelink .. "]]"
		else
			if fallback then
				out = "[[:d:Q" .. qvalue .. "|" .. label .. "]]<abbr title='" .. i18n['wiki-article-not-available'] .. "'>[*]</abbr>"
			else
				out = label
			end
		end
	end
	return out
end

local function roundToDecimalNum(number, decimalPlace)
	local mult = 10^(decimalPlace or 0)
	return math.floor(number * mult + 0.5) / mult
end

function RoundNumber(num, idp)
end

local function findNumberOfDecimals(number)
	-- first, let's make it a string
	local numStr = tostring(tonumber(number))
	-- now let's get the decimals
	local integers,decimals = numStr:match('(%d+)%.(%d+)')
	
	return #decimals
end


-- This is used to get a value
-- if the boolean "list" is set, it will concatenate all available values in a comma-separated list
-- if "dateformat" is set, it will look for qualifier-dates (P585, P580) and include in parathensis and format accordingly to the dateformat
local function getValue(propertyID, list, dateformat, edit, fallback, usesuffix, suffix_label, usebounds, value_from_wiki)
	if value_from_wiki and #value_from_wiki and value_from_wiki ~= "" then
		return value_from_wiki
	else
		local lang = mw.language.getContentLanguage()
		local fallbacklang = mw.language.getFallbacksFor( lang.code )
		local entity = mw.wikibase.getEntityObject()
		local claims
		if entity and entity.claims then
			claims = entity.claims[propertyID]
		end
		local out = {}
		local highestrank = nil
		if claims then
			if (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == "wikibase-entityid") then
				-- if wiki-linked value output as link if possible
				for k, v in pairs(claims) do
					if v.rank ~= mw.wikibase.entity.claimRanks.RANK_DEPRECATED then
						out[#out + 1] = { output = getQValueLink("Q" .. v.mainsnak.datavalue.value["numeric-id"], fallback, fallbacklang) }
						-- now let's check for a suffix
						if usesuffix and v.mainsnak.datavalue.value.unit then
							-- find Q-value
							local qval = ""
							_, _, qval = string.find(v.mainsnak.datavalue.value.unit, "^https*://www.wikidata.org/entity/(Q%d+)$")
							if qval and qval ~= "" then
								local suffix = getQValueLink(qval, fallback, fallbacklang, suffix_label)
								if suffix then
									out[#out].output = out[#out].output .. " " .. suffix
								end
							end
						end
						if dateformat then
							local date = getDate(v.qualifiers, dateformat, lang.code)
							if date then
								out[#out].output = out[#out].output .. " " .. date
							end
						end
						out[#out].rank = v.rank
						out[#out].order = getOrder(v.qualifiers)
						highestrank = compareRank(highestrank, v.rank)
					end
				end
			elseif (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == 'quantity') then
				-- this is a quantity, output as value
				for k, v in pairs(claims) do
					if v.rank ~= mw.wikibase.entity.claimRanks.RANK_DEPRECATED then
						if v.mainsnak.datavalue.value['amount'] then
							out[#out + 1] = { output = lang:formatNum(tonumber(v.mainsnak.datavalue.value['amount'])) }
						end
						-- check for bounds
						if usebounds and v.mainsnak.datavalue.value['lowerBound'] and v.mainsnak.datavalue.value['upperBound'] then
							local lowdiff = tonumber(v.mainsnak.datavalue.value['amount']) - tonumber(v.mainsnak.datavalue.value['lowerBound'])
							local upperdiff = tonumber(v.mainsnak.datavalue.value['upperBound']) - tonumber(v.mainsnak.datavalue.value['amount'])
							local numDec = findNumberOfDecimals(v.mainsnak.datavalue.value['amount'])
							out[#out].output = out[#out].output .. "&nbsp;±" .. tostring(roundToDecimalNum(lowdiff, numDec))
						end
						-- now let's check for a suffix
						if usesuffix and v.mainsnak.datavalue.value.unit then
							-- find Q-value
							local qval = ""
							_, _, qval = string.find(v.mainsnak.datavalue.value.unit, "^https*://www.wikidata.org/entity/(Q%d+)$")
							if qval and qval ~= "" then
								local suffix = getQValueLink(qval, fallback, fallbacklang, suffix_label)
								if suffix then
									out[#out].output = out[#out].output .. " " .. suffix
								end
							end
						end
						if dateformat then
							local date = getDate(v.qualifiers, dateformat, lang.code)
							if date then
								out[#out].output = out[#out].output .. " " .. date
							end
						end
						out[#out].rank = v.rank
						out[#out].order = getOrder(v.qualifiers)
						highestrank = compareRank(highestrank, v.rank)
					end
				end
			elseif (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == 'time') then
				-- this is a quantity, output as value
				for k, v in pairs(claims) do
					if v.rank ~= mw.wikibase.entity.claimRanks.RANK_DEPRECATED then
						if v.mainsnak.datavalue.value['time'] then
							local timeformat = dateformat
							if not timeformat or timeformat == "" then
								timeformat = i18n['standard-timeformat']
							end
							out[#out + 1] = { output = mw.language.new(lang.code):formatDate(timeformat, v.mainsnak.datavalue.value.time) }
						end
						-- now let's check for a suffix
						if usesuffix and v.mainsnak.datavalue.value.unit then
							-- find Q-value
							local qval = ""
							_, _, qval = string.find(v.mainsnak.datavalue.value.unit, "^https*://www.wikidata.org/entity/(Q%d+)$")
							if qval and qval ~= "" then
								local suffix = getQValueLink(qval, fallback, fallbacklang, suffix_label)
								if suffix then
									out[#out].output = out[#out].output .. " " .. suffix
								end
							end
						end
						if dateformat then
							local date = getDate(v.qualifiers, dateformat, lang.code)
							if date then
								out[#out].output = out[#out].output .. " " .. date
							end
						end
						out[#out].rank = v.rank
						out[#out].order = getOrder(v.qualifiers)
						highestrank = compareRank(highestrank, v.rank)
					end
				end
			elseif (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == 'string') then
				-- this is a string
				for k, v in pairs(claims) do
					if v.rank ~= mw.wikibase.entity.claimRanks.RANK_DEPRECATED then
						if v.mainsnak.datavalue.value then
							out[#out + 1] = { output = v.mainsnak.datavalue.value }
						end
						if dateformat then
							local date = getDate(v.qualifiers, dateformat, lang.code)
							if date then
								out[#out].output = out[#out].output .. " " .. date
							end
						end
						out[#out].rank = v.rank
						out[#out].order = getOrder(v.qualifiers)
						highestrank = compareRank(highestrank, v.rank)
					end
				end
			else
				-- Unknown type
				out[#out + 1] = { output = 'unknown type ' .. claims[1].mainsnak.datavalue.type }
			end
		end
		-- Now we'll have to sort the table according to rank and order
		sortTableRankAndOrder(out)
		-- Now we'll make the output
		local i = 0
		local ret = {}
		while i < #out do
			i = i + 1
			-- check if the rank is within the highestrank
			if out[i].rank == highestrank then
				if list then
					list = list - 1
				end
	
				ret[#ret + 1] = out[i].output
				if not list or list < 0 then
					if i < #out then
						-- We still have entries, show it
						ret[#ret + 1] = '<abbr title="' .. i18n['wikidata-more-results-exists'] .. '>…</abbr>'
						break
					end
				end
			end
		end
		return table.concat(ret, ", ")
	end
end

-- This is used to get the name of the property with upper first case
local function getProperty(propertyID, propertyname_from_wiki)
	if propertyname_from_wiki and #propertyname_from_wiki then
		return propertyname_from_wiki
	else
		local lang = mw.language.getContentLanguage()
		local out =  mw.wikibase.label(propertyID)
		return lang:ucfirst(out)
	end
end

-- This is used to get the type of the property
local function getUnitType(propertyID, value_from_wiki)
	if value_from_wiki and #value_from_wiki and value_from_wiki ~= "" then
		return value_from_wiki
	else
		local lang = mw.language.getContentLanguage()
		local fallbacklang = mw.language.getFallbacksFor( lang.code )
		local entity = mw.wikibase.getEntityObject(propertyID)
		local claims
		if entity and entity.claims then
			claims = entity.claims['P2237']
		end
		local out = {}
		local highestrank = nil
		if claims then
			if (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == "wikibase-entityid") then
				-- if wiki-linked value output as link if possible
				for k, v in pairs(claims) do
					if v.rank ~= mw.wikibase.entity.claimRanks.RANK_DEPRECATED then
						local sitelink = mw.wikibase.sitelink("Q" .. v.mainsnak.datavalue.value["numeric-id"])
						local label = mw.wikibase.label("Q" .. v.mainsnak.datavalue.value["numeric-id"])
						if label == nil then
							-- let's try the fallback-languages
							local checkQ = mw.wikibase.getEntityObject("Q" .. v.mainsnak.datavalue.value["numeric-id"])
							if checkQ then
								local i = 0
								while i < #fallbacklang do
									i = i + 1
									label = checkQ:getLabel( fallbacklang[i] )
									if label ~= nil then
										break
									end
								end
									
							end
							if label == nil then
								-- no such luck, fall back to Q-values
								label = "Q" .. v.mainsnak.datavalue.value["numeric-id"]
							end
						end
								
						if sitelink and sitelink ~= label then
							out[#out + 1] = { output = "[[" .. sitelink .. "|" .. label .. "]]" }
						elseif sitelink  then
							out[#out + 1] = { output = "[[" .. sitelink .. "]]" }
						else
							if fallback then
								out[#out + 1] = { output = "[[:d:Q" .. v.mainsnak.datavalue.value["numeric-id"] .. "|" .. label .. "]]<abbr title='" .. i18n['wiki-article-not-available'] .. "'>[*]</abbr>" }
							else
								out[#out + 1] = { output = label }
							end
						end
						out[#out].rank = v.rank
						out[#out].order = getOrder(v.qualifiers)
						highestrank = compareRank(highestrank, v.rank)
					end
				end
			end
		end
		-- Now we'll have to sort the table according to rank and order
		sortTableRankAndOrder(out)
		-- Now we'll make the output
		local i = 0
		local ret = {}
		local list = 0
		while i < #out do
			i = i + 1
			-- check if the rank is within the highestrank
			if out[i].rank == highestrank then
				if list then
					list = list - 1
				end
	
				ret[#ret + 1] = out[i].output
				if not list or list < 0 then
					if i < #out then
						break
					end
				end
			end
		end
		local ent = mw.wikibase.getEntityObject()
		ret[#ret + 1] = dump(ent.claims)
		return table.concat(ret, ", ")
	end
end

function p.getPropertyUnit(frame)
	getArguments(frame)
	return getUnitType(arguments.property, arguments.wiki_value)
end	
	

local function addLinkback(str, property)
	local id = mw.wikibase.getEntityObject()
	if not id then
		return str
	end
	if type(id) == 'table' then
		id = id.id
	end
	
	local class = ''
	if property then
		class = 'wd_' .. string.lower(property)
	end
	local title = i18n['wikidata-linkback-edit']
	local icon = '[%s [[File:Blue pencil.svg|%s|10px|baseline|link=]] ]'
	local url = mw.uri.fullUrl('d:' .. id .. '#' .. property, 'uselang=nb')
	url.fragment = property
	url = tostring(url)
	local v = mw.html.create('span')
		:addClass(class)
		:wikitext(str)
		:tag('span')
			:addClass('noprint plainlinks wikidata-linkback')
			:css('padding-left', '.5em')
			:wikitext(icon:format(url, title))
		:allDone()
	return tostring(v)
end

--
-- Denne funksjonen kalles opp slik: {{#invoke:WikidataIB|rowProperty|P26}}
-- den vil da returnere en tekst-streng som er en rad i en infoboks slik: <tr class="rad" valign="top"><th colspan="2">[Property-navn]</th><td colspan="2">[claim]</td></tr>
-- property-navn og claim hentes fra wikidata
-- andre argumenter som kan benyttes:
--  property - angir hvilken property som skal benyttes, alternativ måte å spesifisere på: {{#invoke:WikidataIB|rowProperty|property=P26}}
--  liste    - angir at hvis det er flere verdier under en property skal de listes ut slik: <tr class="rad" valign="top"><th colspan="2">[Property-navn]</th><td colspan="2">[claim1], [claim2], ...</td></tr>
--             angis slik: {{#invoke:WikidataIB|rowProperty|P26|liste=1}}
--  dato     - angir om verdiene skal legges inn med dato i parantes i etterkant slik: <tr class="rad" valign="top"><th colspan="2">[Property-navn]</th><td colspan="2">[claim] (dato)</td></tr>
--             angis slik: {{#invoke:WikidataIB|rowProperty|P26|dato=<datoformat>}}, f.eks. {{#invoke:WikidataIB|rowProperty|P26|dato=j. F Y}} vil gi (31. desember 2015)
--             denne kan også kombineres med "liste"
--  verdi    - standardverdi som benyttes dersom wikidata ikke har noen claims/verdier for den angitte egenskapen (property). Må da også spesifiseres sammen med "propertynavn".
--             angis slik: {{#invoke:WikidataIB|rowProperty|P26|verdi=Ola Normann}}
--  pnavn    - egenskapsnavn som benyttes dersom wikidata ikke har den angitte egenskapen (property).
--             angis slik: {{#invoke:WikidataIB|rowProperty|P26|pnavn=Ektefelle}}
--  noedit   - angir om en "editeringslink" som peker mot Wikidata IKKE skal legges på raden. Kun tilgjengelig hvis alle data hentes fra Wikidata.
--             angis slik: {{#invoke:WikidataIB|rowProperty|P26|noedit=1}}
--  fallback - angir om lenker skal opprettes mot wikidata hvis lokal artikkel ikke finnes
--             angis slik: {{#invoke:WikidataIB|rowProperty|P26|fallback=1}}
--

function p.rowProperty(frame)
	getArguments(frame)
	local value = getValue(arguments.property, arguments.list, arguments.date, arguments.edit, arguments.fallback, arguments.usesuffix, arguments.suffix_from_wiki, arguments.usebounds, arguments.wiki_value)
	local retval = ""
	if value and value ~= "" then
		retval = retval .. '<tr class="rad" valign="top"><th colspan="2">' .. getProperty(arguments.property, arguments.label) .. '</th><td colspan="2"'
		if arguments.list then
			retval = retval .. ' class="reducible"'
		end
		retval = retval .. '>'
		if arguments.edit and (not arguments.wiki_value or arguments.wiki_value == "") then
			retval = retval .. addLinkback(value, arguments.property)
		else
			retval = retval .. value
		end
		retval = retval .. '</td></tr>'
	end
	return retval
end


function p.getPropertyValue(frame)
	-- Get argument "property"
	getArguments(frame)
	local value = getValue(arguments.property, arguments.list, arguments.date, arguments.edit, arguments.fallback, arguments.usesuffix, arguments.suffix_from_wiki, arguments.usebounds, arguments.wiki_value)
	local retval = ""
	if value and value ~= "" then
		if arguments.edit and (not arguments.wiki_value or arguments.wiki_value == "") then
			retval = addLinkback(value, arguments.property)
		else
			retval = value
		end
	end
	return retval
end

p.getValue = function(frame)
	local propertyID = mw.text.trim(frame.args[1] or "")

	local success, errorOrEntity, props = parseInput(frame, frame.args[2], propertyID)
	if not success then
		return errorOrEntity
	end
	local function filter(claim)
		return true
	end
	return _getvalue(frame, errorOrEntity, props, filter, propertyID)
end

function p.getPropertyLabel(frame)
	getArguments(frame)
	return getProperty(arguments.property, arguments.label)
end

function p.dumpProperty(frame)
	getArguments(frame)
	local entity = mw.wikibase.getEntityObject(arguments.property) 
	return dump(entity)
end

function p.dumpClaims(frame)
	getArguments(frame)
	local entity = mw.wikibase.getEntityObject()
	local claims = nil
	local retval = ""
	if entity and entity.claims and arguments.property then
		claims = entity.claims[arguments.property]
	elseif entity and entity.claims then
		claims = entity.claims
	end
	if claims then
		retval = dump(claims)
	end
	return retval
end

return p