Module:ComparisonFromDataItem
Jump to navigation
Jump to search
This documentation is transcluded from Module:ComparisonFromDataItem/doc. (Edit | history)
Note to editors: Please don't categorize this template by editing it directly. Instead, place the category in its documentation page, in its "includeonly" section.
Note to editors: Please don't categorize this template by editing it directly. Instead, place the category in its documentation page, in its "includeonly" section.
Usage
This module is used as a pass-through.
It processes input from Template:MultipleTagging and displays the result by using Template:MultipleTaggingDescription.
See also
local getArgs = require('Module:Arguments').getArgs
local titleParser = require('Module:OsmPageTitleParser')
local data = mw.loadData('Module:DescriptionFromDataItem/data')
local i18n = data.translations
local p = {}
-- ##########################################################################
-- CONSTANTS
-- ##########################################################################
-- "fallback" - if this property is not set on the Tag item, check the corresponding Key item
-- "qid" - for item values, output item's Q ID
-- "en" - for item values, output english label
-- "map" - converts claim's value to the corresponding value in the given map
p.INSTANCE_OF = { id = 'P2', qid = true }
p.GROUP = { id = 'P25', fallback = true }
p.RENDER_IMAGE = { id = 'P38', fallback = true }
p.Q_EXCEPT = { id = 'P27' }
p.Q_LIMIT = { id = 'P26' }
p.KEY_ID = { id = 'P16', fallback = true }
p.TAG_ID = { id = 'P19' }
p.TAG_KEY = { id = 'P10' }
p.REL_ID = { id = 'P41' }
p.REL_TAG = { id = 'P40' }
p.ROLE_REL = { id = 'P43' }
p.WIKIDATA = { id = 'P12' }
p.INCOMPATIBLE_WITH = { id = 'P44', fallback = true, multi = true, strid = true }
p.IMPLIES = { id = 'P45', multi = true, strid = true }
p.COMBINATION = { id = 'P46', multi = true, strid = true }
p.SEE_ALSO = { id = 'P18', multi = true, strid = true }
p.REQUIRES = { id = 'P22', multi = true, strid = true }
p.STATUS_REF = { id = 'P11', is_reference = true }
p.IMG_CAPTION = { id = 'P47', is_qualifier = true }
p.STATUS = { id = 'P6', en = true, extra = p.STATUS_REF }
p.IMAGE = { id = 'P28', extra = p.IMG_CAPTION }
-- ##########################################################################
-- UTILITIES
-- ##########################################################################
local function startswith(self, str)
return self:sub(1, #str) == str
end
local formatKeyVal = function(key, value)
if value then
return key .. '=' .. value
else
return key
end
end
local function localize(key, langCode, params)
local msgTable = i18n[key]
local msg
if msgTable then
msg = msgTable[langCode] or msgTable['en']
end
if not msg then
return '<' .. key .. '>'
end
return mw.message.newRawMessage(msg, unpack(params or {})):plain()
end
-- Format as an edit link. Target is either a relative url that starts with a slash, or an item ID (e.g. Q104)
local function editLink(self, target, msgKey)
local file
if msgKey == 'desc_edit_mismatch_page' then
file = 'Red pencil.svg'
else
file = 'Arbcom ru editing.svg'
end
if not startswith(target, '/') then
target = 'Item:' .. target
end
return (' <span class=wb-edit-pencil>[[File:' .. file .. '|12px|' ..
localize(msgKey, self.langCode) .. '|link=' .. target .. ']]</span>')
end
-- ##########################################################################
-- DATA ITEM PARSING
-- ##########################################################################
-- p.Q_LIMIT "limited to region qualifier" if qualifier is present, include the statement
-- only if self.region equals any of the listed regions
-- p.Q_EXCEPT "excluding region qualifier" if qualifier is present, include the statement
-- only if self.region does not equal all of the listed regions
local regionQualifiers = { { prop = p.Q_LIMIT, include = true }, { prop = p.Q_EXCEPT, include = false } }
-- Test if qualifiers indicate that current statement should be
-- included or excluded based on the rules table
-- Returns true/false if it should be included, and true/false if it was based on qualifiers
local function allowRegion(region, statement)
if statement.rank ~= 'preferred' and statement.rank ~= 'normal' then
return false, false
end
local qualifiers = statement.qualifiers
if qualifiers then
for _, value in pairs(regionQualifiers) do
local qualifier = qualifiers[value.prop.id]
if qualifier then
local include = not value.include
for _, q in pairs(qualifier) do
if region == q.datavalue.value.id then
include = value.include
end
end
-- return after the first found rule, because multiple rules
-- do not make any sense on the same statement
return include, true
end
end
end
return true, false -- by default, the statement should be included
end
local function qidToStrid(qid)
local entity = p.wbGetEntity(qid)
if not entity then return end
local tag = p.getClaimValue(p.TAG_ID, 'en', entity)
local eKey, eValue = titleParser.splitKeyValue(tag)
if not eKey then
eKey = p.getClaimValue(p.KEY_ID, 'en', entity)
if eKey then
return { eKey }
end
else
return { eKey, eValue }
end
end
-- Convert claim value into a string
-- property object specifies what value to get:
-- 'qid' - returns data item id
-- 'strid' - return referenced item
-- 'map' - use a map to convert qid into a string
-- 'en' - only english label
-- default - first try local, then english, then qid
local function claimToValue(datavalue, prop, langCode)
local result = false
if datavalue.type == 'wikibase-entityid' then
local qid = datavalue.value.id
if prop.map then
result = prop.map[qid]
elseif prop.strid then
result = qidToStrid(qid)
if not result then
result = { 'Bad item: ' .. qid }
end
elseif not prop.qid then
if not prop.en then
result = p.wbGetLabelByLang(qid, langCode)
end
if not result then
result = p.wbGetLabel(qid)
end
end
if not result then
result = qid
end
elseif datavalue.type == 'string' then
result = datavalue.value
else
-- TODO: handle other property types
result = "Unknown datatype " .. datavalue.type
end
return result
end
local function getStatements(entity, prop)
if prop.multi then
return entity:getBestStatements(prop.id)
else
return entity:getAllStatements(prop.id)
end
end
-- From a monolingual property, get either the given language or English
local function getMonoString(snakList, langCode)
local enVal, val
if snakList then
for _, snak in pairs(snakList) do
local lang = snak.datavalue.value.language
val = snak.datavalue.value.text
if langCode == lang then
return val
elseif langCode == 'en' then
enVal = val
end
end
end
return enVal or val
end
-- Debug: =mw.text.jsonEncode(p.getClaimValue(p.GROUP, 'en', mw.wikibase.getEntity('Q501')),0)
function p.getClaimValue(prop, langCode, entity, fallbackEntity)
local usedFallback = false
local region = data.regions[langCode]
local statements = getStatements(entity, prop)
if fallbackEntity and prop.fallback and next(statements) == nil then
usedFallback = true
statements = getStatements(fallbackEntity, prop)
end
if prop.multi then
local result = {}
for _, stmt in pairs(statements) do
local val = claimToValue(stmt.mainsnak.datavalue, prop, langCode)
if val then
table.insert(result, val)
end
end
return result
end
local match
for _, stmt in pairs(statements) do
local include, qualified = allowRegion(region, stmt)
if include then
match = stmt
if qualified then
-- Keep non-qualified statement until we look through all claims,
-- if we see a qualified one (limited to the current region), we found the best match
break
end
end
end
local result
local extra
if match then
-- Get extra value if available (e.g. reference or image caption)
if prop.extra then
if prop.extra.is_reference and match.references then
for _, ref in pairs(match.references) do
local snak = ref.snaks[prop.extra.id]
if snak and snak[1] then
extra = snak[1].datavalue.value
break
end
end
elseif prop.extra.is_qualifier and match.qualifiers then
extra = getMonoString(match.qualifiers[prop.extra.id])
end
end
result = claimToValue(match.mainsnak.datavalue, prop, langCode)
end
return result, usedFallback, extra
end
-- Get categories string, e.g. "[[category:xx]][[category:yy]]"
local function constructor(args)
local self = {
categories = {},
args = args,
}
self.currentTitle = mw.title.getCurrentTitle()
-- sets self.key, self.value, and self.language from the current title
titleParser.parseTitleToObj(self, self.currentTitle)
-- if lang parameter is set, overrides the one detected from the title
if args.lang and mw.language.isSupportedLanguage(args.lang) then
self.language = mw.getLanguage(args.lang)
end
self.langCode = self.language:getCode()
toSitelink = function(key, value)
return (value and 'Tag:' or 'Key:') .. formatKeyVal(key, value)
end
local entity1 = p.wbGetEntity(p.wbGetEntityIdForTitle(toSitelink(args.key1, args.value1)))
local entity2 = p.wbGetEntity(p.wbGetEntityIdForTitle(toSitelink(args.key2, args.value2)))
self.args.status1 = p.getClaimValue(p.STATUS, 'en', entity1)
self.args.status2 = p.getClaimValue(p.STATUS, 'en', entity2)
self.args.statusLink1 = args.statusLink1
self.args.statusLink2 = args.statusLink2
self.args.extra1 = args.extra1
self.args.extra2 = args.extra2
if args.key3 then
local entity3 = p.wbGetEntity(p.wbGetEntityIdForTitle(toSitelink(args.key3, args.value3)))
self.args.status3 = p.getClaimValue(p.STATUS, 'en', entity3)
self.args.statusLink3 = args.statusLink3
self.args.extra3 = args.extra3
end
if args.key4 then
local entity4 = p.wbGetEntity(p.wbGetEntityIdForTitle(toSitelink(args.key4, args.value4)))
self.args.status4 = p.getClaimValue(p.STATUS, 'en', entity4)
self.args.statusLink4 = args.statusLink4
self.args.extra4 = args.extra4
end
if args.key5 then
local entity5 = p.wbGetEntity(p.wbGetEntityIdForTitle(toSitelink(args.key5, args.value5)))
self.args.status5 = p.getClaimValue(p.STATUS, 'en', entity5)
self.args.statusLink5 = args.statusLink5
self.args.extra5 = args.extra5
end
return self
end
-- ##########################################################################
-- ENTRY POINTS
-- ##########################################################################
-- If we found data item for this key/tag, compare template parameters
-- with what we have in the item, and add some extra formatting/links/...
-- If the values are not provided, just use the ones from the data item
function p.main(frame)
if not mw.wikibase then
return frame:expandTemplate { title = 'Warning', args = { text = "The OSM wiki is experiencing technical difficulties. Infoboxes will be restored soon." } }
end
local args = getArgs(frame)
-- initialize self - parse title, language, and get relevant entities
local self = constructor(args)
return frame:expandTemplate { title = 'Template:MultipleTaggingDescription', args = args }
end
-- ##########################################################################
-- DEBUGGING AND TESTING SUPPORT
-- ##########################################################################
-- These methods could be overwritten by unit tests
function p.wbGetEntity(entity)
return mw.wikibase.getEntity(entity)
end
function p.wbGetEntityIdForTitle(title)
return mw.wikibase.getEntityIdForTitle(title)
end
function p.wbGetLabel(qid)
return mw.wikibase.getLabel(qid)
end
return p