Modul:Reference score

Fra Wikipedia, den frie encyklopedi
Moduldokumentasjon
---Module for chosing the best reference.

--@table for localization
local i18n = mw.loadData( 'Module:Reference score/i18n' )

-- @table for configuration
local conf = mw.loadData( 'Module:Reference score/conf' )

local labeltekst = {
	["P50"] = "", -- forfatter
	["P123"] = "", -- utgiver
	["P248"] = "", -- nevnt i
	["P577"] = "utgitt ", -- utgivelsesdato
	["P813"] = "besøkt ", -- besøksdato
	["P854"] = "",  -- referanse-URL
	["P2093"] = ""  -- forfatternavn
}
local wait = {
	["P577"] = true, -- utgivelsesdato
	["P813"] = true, -- besøksdato
	["P1683"] = true, -- sitat
	["Pxxxx"] = true   -- dummy
}
local checkLabel = {
	["P1810"] = true,  -- oppført som
	["Pxxxx"] = true   -- dummy
}
local unknownItem = 0
local done = {}
local reftxt = ""
local sep = ""


local function norskDatoFraClaim(claim)
	if claim.snaktype ~= "value" then
		return "ingen verdi"
	end
	local value = claim.datavalue.value
	local presisjon = value.precision
	local aar,_,maaned,dag,fkr,_ = splitTimestamp(value.time)
	local utstring = ''

	if presisjon == 11 then
		utstring = string.format( '%s %s %s%s', dag, maaned, tonumber(aar) or aar, fkr )
	elseif presisjon == 10 then
		utstring = string.format( '%s %s%s', maaned, tonumber(aar) or aar, fkr )
	elseif presisjon == 9 then
		utstring = (tonumber(aar) or aar)..''..fkr
		utstring = string.format( '%s%s', tonumber(aar) or aar, fkr )
	elseif presisjon == 8 then
		utstring = string.format( '%s-årene%s',  math.floor(((tonumber(aar) or 0))/10)*10, fkr ) 
	elseif presisjon == 7 then
		utstring = string.format( '%s. århundre%s', math.floor(((tonumber(aar) or 0)+99)/100), fkr )
	end

	return mw.text.nowiki( utstring ).. kalender(value.calendarmodel)
end

function addRefTxt(pidLabel,refValue)
		reftxt = reftxt .. sep .. "  " .. pidLabel .. refValue 
		--  .. " ( " .. datatype .. " )"
		sep = ", "
end

local function formatUrl(url)
	local reftext = url
	local j1 = string.find(reftext,'//',1,true)
	if j1 then reftext = string.sub(reftext,j1+2,string.len(reftext)) else reftext = '' end
	if reftext ~= '' then
		local i1 = string.find(reftext,'/',1,true)
		if i1 then reftext = string.sub(reftext,1,i1-1) end
	else
		reftext = url
	end
	return "[" .. url .. " " .. reftext .. "]"
end

local function formatItem(property)
	local item = nil
	if property[1].snaktype == "value" then
		item = property[1].datavalue.value or nil
	end
	if item then
		local value = item.id
		local label = mw.wikibase.label( value )
		local sitelink = mw.wikibase.sitelink( value )
		if sitelink or label then
			return mw.wikibase.formatValues(property)
		end
		unknownItem = unknownItem +1
		return '[[d:' .. value .. '|' .. value .. ']]'
	else
		return "ingen verdi"
    end
end

function fmtTitle(ref)
	if done["P1476"] then 
		return 
	end
	done["P1476"] = true
	title = ref["P1476"] or nil
	if title then
		local refValue = "«" .. mw.wikibase.formatValues(title) .. "»"
		if ref["P854"] then
			refValue = "[" .. ref["P854"][1].datavalue.value .. " " .. refValue .. "]"
			done["P854"] = true
		end
		addRefTxt("",refValue)
	end
	return 
end

function fmtQuote(pid,ref)
	if done[pid] then 
		return 
	end
	done[pid] = true
	local quote = ref[pid] or nil
	if quote then
		local refValue = "«" .. mw.wikibase.formatValues(quote) .. "»"
		addRefTxt("",refValue)
	end
	return 
end

function fmtTekst(pid,ref)
	if done[pid] then 
		return 
	end
	done[pid] = true
	local tekst = ref[pid] or nil
	if tekst then
		local refValue = mw.wikibase.formatValues(tekst)
		addRefTxt("",refValue)
	end
	return 
end

-- Ignore cases where the value is just the label.
function fmtLlabelCheck(pid,ref)
	if done[pid] then 
		return 
	end
	done[pid] = true
	local refItem = ref[pid] or nil
	if refItem then
		local refValue = mw.wikibase.formatValues(refItem)
		local pidLabel = labeltekst[pid] or mw.wikibase.getLabel(pid) .. " "
		local label = mw.wikibase.label()
		local refString = ref[pid][1].datavalue.value
		if not (label == refString ) then     
			addRefTxt(pidLabel,refValue)
		end
	end
	return 
end

function fmtItem(pid,ref)
	if done[pid] then 
		return 
	end
	local property = ref[pid] or nil
	if property then
		local pidLabel = labeltekst[pid] or mw.wikibase.getLabel(pid) .. " "
		local refValue = formatItem(property)
		addRefTxt(pidLabel,refValue)
	end
	done[pid] = true
end

function fmtDato(pid,ref)
	if done[pid] then 
		return 
	end
	local property = ref[pid] or nil
	if property then
		local pidLabel = labeltekst[pid] or mw.wikibase.getLabel(pid) .. " "
		local refValue = norskDatoFraClaim(property[1])
		addRefTxt(pidLabel,refValue)
	end
	done[pid] = true
end

function fmtMisc(ref)
	for pid, property in pairs(ref) do
		if checkLabel[pid] then
			fmtLlabelCheck(pid,ref)
		elseif not done[pid] and not wait[pid] then
			local datatype = property[1].datatype or "xxx"
			local pidLabel = labeltekst[pid] or mw.wikibase.getLabel(pid) .. " "
			local refValue = mw.wikibase.formatValues(property)
			if property[1].snaktype ~= "value" then
				refValue = datatype .. ": Ingen verdi"
			else
				if datatype == 'url' then
				 	refValue = formatUrl(property[1].datavalue.value)
				elseif datatype == 'time' then
					refValue = norskDatoFraClaim(property[1])
				elseif datatype == 'wikibase-item' then
					refValue = formatItem(property)
				elseif datatype == 'commonsMedia' then
					refValue = string.format([[
						<div class="mw-collapsible mw-collapsed">
							<div>%s</div>
							<div class="mw-collapsible-content">%s</div>
						</div>
						]],property[1].datavalue.value ,refValue)
				end
			end
		    addRefTxt(pidLabel,refValue)
			done[pid] = true
		end
	end
end

function fmtSingle(ref)
	local num = 0
	for pid, property in pairs(ref) do
		num = num+1
	end

	if num~=1 then
		 return false
	end
	if not ref["P248"] then
		return false
	end
	local P248 = ref["P248"]
	local item = nil
	if P248[1].snaktype == "value" then
		item = P248[1].datavalue.value or nil
	end
	if item then
		local value = item.id
		local label = mw.wikibase.label( value )
		local description = mw.wikibase.description( value )
		local sitelink = mw.wikibase.sitelink( value )
		if label and not sitelink then
			local txt ="«" .. label .. "», " .. description
			addRefTxt("",txt)
			return true
		end
	end
	return false
end

function fmtnew(ref)
	reftxt = ""
	sep = ""
	local args = mw.getCurrentFrame():getParent().args
	if args and args["wikidatatesting"] == "ref" then
		if fmtSingle(ref) then
			return reftxt
		end
	end
	local refValue = nil
	fmtItem("P123",ref) -- utgiver
	fmtItem("P248",ref) -- nevnt i
	fmtItem("P50",ref)  -- fortatter
	fmtTekst("P2093",ref)  -- fortatternavn
	fmtTitle(ref)
    fmtMisc(ref)
	fmtDato("P577",ref)  -- utgivelsesdato
	fmtDato("P813",ref)  -- besøksdato
	fmtQuote("P1683",ref) -- sitat
	return reftxt .. "<sup><small>[Hentet fra Wikidata]</small></sup>"
	-- return mw.wikibase.formatValues( ref )
	-- "<pre>" .. mw.text.jsonEncode(ref, mw.text.JSON_PRETTY) .. "</pre>"
end

function fmt(ref)
	done = {}
	reftxt = ""
	sep = ""
	-- local args = mw.getCurrentFrame():getParent().args
--	if args and args["wikidatatesting"] == "ref" then
		return fmtnew(ref)
--	end
--	return mw.wikibase.formatValues( ref )
end 

-- @table for export
local h = {}

--- Make a list of categories.
-- Note this probably should be moved out of this module.
-- @tparam number num of references
-- @treturn table of strings
function h.makeCategories( num )
	local t = {}
	if num and num > 0 then
		table.insert( t, mw.message.newRawMessage( i18n['category-pages-using-references-from-statement'] ):plain() )
		if num == 1 then
			table.insert( t, mw.message.newRawMessage( i18n['category-pages-using-single-reference-from-statement'] ):plain() )
		elseif num > 1 then
			table.insert( t, mw.message.newRawMessage( i18n[string.format('category-pages-using-%d-references-from-statement', num )] ):plain() )
		end
	end
	return t
end

--- Generate a language score for the claims.
-- @tparam table claims to process
-- @treturn number best score
function h.scoreLanguage( claims )
	local keep = conf.lowScore
	for _,v in ipairs( claims ) do
		if v.snaktype == 'value'
				and v.datatype == 'monolingualtext'
				and v.datavalue.type == 'monolingualtext' then
			local score = conf.languageScore[v.datavalue.value.language]
			if score then
				-- note that higher number means lower prority
				keep = (keep < score) and keep or score
			end
		end
	end
	return keep
end

--- Generate a root domain score for the claims.
-- @tparam table claims to process
-- @treturn number best score
function h.scoreDomain( claims )
	local keep = conf.lowScore
	for _,v in ipairs( claims ) do
		if v.snaktype == 'value'
				and v.datatype == 'url'
				and v.datavalue.type == 'string' then
			local uri = mw.uri.new( v.datavalue.value )
			local root = string.match( uri.host, '%.([^.]+)$' )
			local score = conf.domainScore[root]
			if score then
				-- note that higher number means lower prority
				keep = (keep < score) and keep or score
			end
		end
	end
	return keep
end

--- Generate an entity score for the claims.
-- @tparam table claims to process
-- @treturn number best score
function h.scoreEntity( claims )
	local keep = conf.lowScore
	for _,v in ipairs(claims) do
		if v.snaktype == 'value'
				and v.datatype == 'wikibase-item'
				and v.datavalue.type == 'wikibase-entityid' then
			local score = conf.entityScore[v.datavalue.value.id]
			if score then
				-- note that higher number means lower prority
				keep = (keep < score) and keep or score
			end
		end
	end
	return keep
end

--- Generate a property score for the claims.
-- @tparam table claims to process
-- @treturn number best score
function h.scoreProperty( claims )
	local keep = conf.lowScore
	for _,v in ipairs(claims) do
		-- strictly speaking this could be dropped as all should be equal
		local score = conf.propertyScore[v.property]
		if score then
			-- note that higher number means lower prority
			keep = (keep < score) and keep or score
		end
	end
	return keep
end

--- Generate scores for the references.
-- Note that the generated list is sparse, and is using false as marker.
-- @tparam table list to process
-- @treturn table of references
function h.score( list )
	-- make sure we have a continuous target list
	local t = {}
	for i=1,conf.lowScore do
		t[i] = false
	end
	-- loop over the source list
	for _,v in ipairs( list ) do
		-- is the reference excluded?
		local exclude = false
		for _,w in ipairs( conf.exclude ) do
			if v.snaks and v.snaks[w] then
				exclude = true
				break
			end
		end
		-- only process included (ie not excluded) references
		if not exclude then
			local keep = conf.lowScore
			-- try language of "title"
			if v.snaks.P1476 then
				local score = h.scoreLanguage(v.snaks.P1476)
				if score then
					-- note that higher number means lower prority
					keep = (keep < score) and keep or score
				end
			end
			-- try root domain of "reference url"
			if v.snaks.P854 then
				local score = h.scoreDomain(v.snaks.P854)
				if score then
					-- note that higher number means lower prority
					keep = (keep < score) and keep or score
				end
			end
			-- try reference to entity
			for _,w in pairs(v.snaks) do
				local score = h.scoreEntity( w )
				if score then
					-- note that higher number means lower prority
					keep = (keep < score) and keep or score
				end
			end
			-- some properties that usually imply somewhat quality
			for _,w in pairs(v.snaks) do
				local score = h.scoreProperty( w )
				-- note that higher number means lower prority
				keep = (keep < score) and keep or score
			end
			table.insert( t, keep, v )
		end
	end
	return t
end

--- Compact the sparse list.
-- Note that the input list is sparse, and using false as marker.
-- @tparam table list of any
-- @tparam[limit=conf.maxRefs] nil|number limit for truncation of list
-- @treturn table
function h.compact( list, limit )
	limit = limit or conf.maxRefs
	local t = {}
	local counter = 0
	for _,v in ipairs( list ) do
		if v then
			counter = counter + 1
			if limit and counter <= limit then
				table.insert( t, v )
			elseif not limit then
				table.insert( t, v )
			end
		end
	end
	return t
end

--- Render references.
-- @tparam table frame
-- @tparam table list of references
-- @treturn string
function h.render( frame, list )
	local scored = h.score( list or {} )
	local compacted = h.compact( scored )
	local wiki = ''
	local hits = 0
	for i,v in ipairs( compacted ) do
		hits = hits + 1
		local keys = {}
		for k,_ in pairs( v.snaks ) do
			table.insert( keys, k )
		end
		local snaks = {}
		for _,k in pairs( mw.wikibase.orderProperties( keys ) ) do
			-- lua keeps injection order
			snaks[k] = v.snaks[k]
		end
		-- hash will merge similar entries
		-- local content = mw.wikibase.formatValues( snaks )
		local content = fmt(snaks)
		local attrs = { name = string.format( 'hash-%s', v.hash ) }
		wiki = wiki .. frame:extensionTag( 'ref', content, attrs )
	end
	for _,v in ipairs( h.makeCategories( hits ) ) do
		wiki = wiki .. mw.ustring.format('[[Category:%s]]', v )
	end
	if unknownItem >0 then
		wiki = wiki .. "[[Kategori:Artikler hvor referanser mangler oversettelse]]"
	end
	return wiki
end

return h