Module:PropertyChain/sandbox

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Lua
CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules

Documentation for this module may be created at Module:PropertyChain/sandbox/doc

Code

--[[  
  __  __           _       _        ____                            _          ____ _           _       
 |  \/  | ___   __| |_   _| | ___ _|  _ \ _ __ ___  _ __   ___ _ __| |_ _   _ / ___| |__   __ _(_)_ __  
 | |\/| |/ _ \ / _` | | | | |/ _ (_) |_) | '__/ _ \| '_ \ / _ \ '__| __| | | | |   | '_ \ / _` | | '_ \ 
 | |  | | (_) | (_| | |_| | |  __/_|  __/| | | (_) | |_) |  __/ |  | |_| |_| | |___| | | | (_| | | | | |
 |_|  |_|\___/ \__,_|\__,_|_|\___(_)_|   |_|  \___/| .__/ \___|_|   \__|\__, |\____|_| |_|\__,_|_|_| |_|
                                                   |_|                  |___/                           
Maintainers:
 * Jarekt

]]

local getLabel = require('Module:Wikidata label')._getLabel		-- get localized translations of date formats
local core = require('Module:Core')		-- get localized translations of date formats

-------------------------------------------------------------------------------
local function getProperty(entity, prop)
	return (core.parseStatements(entity:getBestStatements( prop ), nil) or {nil})[1]
end

-------------------------------------------------------------------------------
local function getItemProperty(item, prop)
	return (core.parseStatements(mw.wikibase.getBestStatements( item, prop ), nil) or {nil})[1]
end

-------------------------------------------------------------------------------
local function getItemAllPropertyValues(item, prop)
	return (core.parseStatements(mw.wikibase.getBestStatements( item, prop ), nil) or nil)
end

-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}

--[[ ========================================================================================
Follow and recored a chain of properties:
 * item - starting item id
 * prop - property to follow
 * endItem - (optional) item id used as a stopping criteria when encountered
 * link - (optional) link type: wikidata, wikipedia (default) , commons, commonscat etc. same as in "Module:Wikidata label"
 * separator - (optional with '→' default) symbol or string used to separate items in the list
 * lang - (optional defaulting to user's language) language of the labels
]]
function p._PropertyChain(item, prop, endItem, link, separator, lang)	
	if not (lang and mw.language.isSupportedLanguage(lang)) then 
		lang =  mw.getCurrentFrame():callParserFunction( "int", "lang" ) -- get user's chosen language 
	end
	local chain, items = {}, {}
	for iter = 1,50 do -- at maximum only 50 iterations are allowed
		local entity = mw.wikibase.getEntity(item)
		table.insert(chain, getLabel(entity, lang, link) )
		items[entity.id] = true -- early detection of loops
		item = getProperty(entity, prop)
		if not item or items[item] or item==endItem then
			-- stopping criteria: item is missing property "prop", or a loop is detected or end-item ID was found
			break
		end
	end
	if item and item==endItem then
		table.insert(chain, getLabel(item, lang, link) )
	end
	return table.concat(chain, separator or '→')
end

--[[ ========================================================================================
Follow a chain of properties until endProp1 or endProp2 encountered and return that value :
 * item - starting item id
 * prop - property to follow
 * endProp1, endProp2 - properties whose presence is the stopping criteria
 *                      endProp2 is optional
]]
function p._PropertyChain2(item, prop, endProp1, endProp2)
	local items = {}
	for iter = 1,50 do -- at maximum only 50 iterations are allowed
		local pp = getItemProperty(item, prop)
		local ep = getItemProperty(item, endProp1)
		if ep then
			return ep
		else if endProp2 then
				ep = getItemProperty(item, endProp2)
				if ep then
					return ep
				end
			end
		end
		
		if not pp or items[pp] then
			return nil -- loop detected or prop missing
		else
			items[pp] = true -- early detection of loops
			item = pp
		end
	end
end

--[[ ========================================================================================
Follow multiple chains of properties until either endProp1 or endProp2 is encountered and return a table of all values found :
 * item - starting item id
 * parentProp - property to follow up the tree
 * endProp1, endProp2 - properties whose presence is the stopping criteria
 *                      endProp2 is optional
 * alreadyTraversed - a table of already visited items
 * maxDepth - maximum depth to avoud infinite loop
]]
function p._recursiveItemProperties (item, parentProp, endProp1, endProp2, alreadyTraversed, maxDepth)
    if maxDepth == 0 then
		return {}
	else
		-- endProp found, return all values
		local ep = getItemAllPropertyValues(item, endProp1)
		if ep then
			return ep
		else if endProp2 then
				ep = getItemAllPropertyValues(item, endProp2)
				if ep then
					return ep
				end
			end
		end
		
		if alreadyTraversed[item] then
		-- loop detected
			return {}
		else
			alreadyTraversed[item] = true -- early detection of loops, before recursive call
		end
		
		-- traverse parentProp(s) tree
		local pp = getItemAllPropertyValues(item, parentProp)
		local res = {}
		for _,ppv in ipairs(pp) do
			for _,v in ipairs(p._recursiveItemProperties(ppv, parentProp, endProp1, endProp2, alreadyTraversed, maxDepth-1)) do
				table.insert(res, v)
			end
		end

		-- make res unique
		local hash = {}
		local resUnique = {}
		
		for _,v in ipairs(res) do
		   if (not hash[v]) then
		       resUnique[#resUnique+1] = v 
		       hash[v] = true
		   end

		end
		return resUnique
	end
end

--[[ ========================================================================================
get all ISO 3166-2 or ISO 3166 1 codes for an item :
 * item - starting item id
 * separator - separator for concat multiple values
]]
function p._AllISO3166Codes(item, separator)
	local alreadyTraversed = {} -- loop detection
	local maxDepth = 10 -- at maximum only maxDepth iterations are allowed
	local items = p._recursiveItemProperties(item, 'P131', 'P300', 'P297', alreadyTraversed, maxDepth)
	return table.concat(items, separator or ', ')
end

function p.PropertyChain(frame)
	local args = core.getArgs(frame)
	
	-- find stopping item ID
	local endItem = args.endqid
	if not endItem and args.endpid then
		endItem = getProperty(mw.wikibase.getEntity(args.qid), args.endpid, 'one')
	end
	return p._PropertyChain( args.qid, args.pid, endItem, args.link, args.separator, args.lang )	
end

function p.PropertyChain2(frame)
	local args = core.getArgs(frame)
	return p._PropertyChain2( args.qid, args.pid, args.endpid1, args.endpid2 )
end

function p.AllISO3166Codes(frame)
	local args = core.getArgs(frame)
	return p._AllISO3166Codes( args.qid, args.sep )
end

--[[ ========================================================================================
get a ISO 3166-2 or ISO 3166 1 code for an item :
 * item - starting item id
 wrapper for specific _PropertyChain2 call
]]
function p.AISO3166Code(frame)
	local args = core.getArgs(frame)
	return p._PropertyChain2( args.qid, 'P131', 'P300', 'P297' )
end

return p