Module:Sandbox/Was a bee/tree

< Module:Sandbox
Revision as of 20:36, 23 October 2017 by imported>Was a bee
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

UsageEdit

User:Was a bee/TreeBox Paste following code at anatomy articles. It automatically shows tree (as far as structural data is stored in Wikidata with the source, TA98)

{{User:Was a bee/TreeBox}}

PlansEdit

As far as I think in long-term perspective, technical part (rendering graphical tree based on data in Wikidata) would be implemented someday by Mediawiki extension or something in more stylish manner by experienced coder. Just like Template:Category tree (Wikipedia:Catalogue_of_CSS_classes#Page.2Faction_specific, mw:Extension:CategoryTree). So I feel other part would be more important.

  1. Template:Done: Rendering parent-side tree (upstream toward human body)
    1. Need to process multiple-parents cases (bifurcated parent tree)
  2. Template:Done: Rendering brother tree. Parent (upper) and child (lower) concepts are important. But at the same time, brother concepts (same level concepts) are also important for understanding the topic.
  3. Template:Done: Formatting TA98 ref. I think at least it would be needed to provide external link to the entry (like this [1]) using TA98_ID (d:Property_talk:P1323)
  4. Is it possible to make tree references more compact? Because tree references would be needed (WP:V), but actually not so interesting.
  5. Treat species general and human specific articles. Fro example, "Human brain" and "Brain", "Human head" and "Head".
  6. (Re)arrange some input options, especially source and appearance related options.
  7. Populating Wikidata pages with "part_of (d:Property talk:P361)" and "has_part (d:Property talk:P527)" relations (see Ontology_components#Relationships). Currently TA98 ID is registered in about 3.3k Wikidata pages (Property_talk:P1323).

Related pagesEdit

Technical backgroundEdit

Visual appearanceEdit

Tree-like visual appearance is created by the "treeview" style described in MediaWiki:Common.css. This module outputs code which contains HTML elements "ul" (unordered list) and "li" (list). And the style sheet converts them into tree view.

Source example (output from the module) Result (output to reader)
<div class="treeview">
<ul>
<li>[[Alpha]]

<ul>
<li>[[Bravo]]
<ul>[[Charlie Brown|Charlie]]
<li>[[Delta]]</li>
<li class="lastline">[[Echo]]</li>
</li>
</ul>

<ul><li>[[Foxtrot]]
<ul><li class="lastline">[[Golf]]</li></ul>
</li></ul>
<ul><li class="lastline">[[Hotel]] [[File:Blue pencil.svg|frameless|text-top|10px|link=https://www.wikidata.org/wiki/Q27686]]</li></ul>

</ul></li></ul></div>

AlgorithmEdit

SourcingEdit

Currently permitted source

Statement which has following item in "stated in" section is shown in tree. Other data is not shown in tree in default.

Other possible candidates

ProblemsEdit

Incompatibilities between different sources
Different sources categorize things different ways. But this module can show only one tree. So some kind of troubleshooting is needed. Actually tree structures are almost similar and the differences between them are subtle. But it exist certainly.

UsageEdit

Tree based only on data which is sourced by TA98Edit

{{User:Was a bee/Tree|items=Q47273|childProperty=P527|childDepth=10|parentProperty=P361|parentDepth=5|sources=TA98}}

User:Was a bee/Tree

Tree based on data including unreferenced dataEdit

{{User:Was a bee/Tree|items=Q47273|childProperty=P527|childDepth=10|parentProperty=P361|parentDepth=5||sources=}}

User:Was a bee/Tree



local p = {}

datas = {}


-- These setdata() and getdata() functions are used to access (read and write) to "datas" table
function p.setdata(n, key, val)
	if datas[n] == nil then
		datas[n] = {}
	end
	datas[n][key] = val
end

function p.getdata(n, key, defval)
	if  datas[n] == nil or datas[n][key] == nil then
		return defval
	else
		return datas[n][key] 
	end
end

function p.children(query, itemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )
 --------------------------------------------------------------------------------
 -- children function which listing all childrens that certain item has.
 -- Not listing grand childrens, or grand-grand childrens. Listing only childrens.
 -- Final returning value "childrens" is like {Q123123, Q3984198237, Q1874138746} 
 --------------------------------------------------------------------------------
	local childrens = {}
	local entity = mw.wikibase.getEntity( itemId )
	local claims = nil
	local x = 1
	local tmpValue = {}
	
	---------------------------------------------------------------
	-- Start collecting data from the retrieved entity, and save that into the datas[] table.
	-- Because getEntity() function is EXPENSIVE, so here, collecting all data at once.
	-- https://en.wikipedia.org/wiki/WP:EXPENSIVE
	-- https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua#mw.wikibase.getEntity
	----------------------------------------------------------------
	local globalSiteId_1 = lang1 .. 'wiki'
	local globalSiteId_2
	
	if lang2 and lang2 ~= '' then
		globalSiteId_2 = lang2 .. 'wiki'
	else
		globalSiteId_2 = nil
	end
	
	p.setdata(itemId, "sitelink_1", entity:getSitelink( globalSiteId_1 ))
	p.setdata(itemId, "label_1", entity:getLabel( lang1 ))
	if lang2 and lang2 ~= '' then
		p.setdata(itemId, "sitelink_2", entity:getSitelink( globalSiteId_2 ))
		p.setdata(itemId, "label_2", entity:getLabel( lang2 ))
	end
	
	-- ★★★★ Start source specific code ★★★★ --
	------------TA98------------
	claims = entity['claims']['P1323'] --TA98 ID
	if claims then
		for  _,claim in pairs( claims ) do
			tmpValue[x] = claim.mainsnak.datavalue.value
			x = x + 1
		end
		p.setdata(itemId, "TA98", tmpValue)
	end
	
	tmpValue = {} --initialize
	x = 1 --initialize
	claims = nil --initialize
	
	------------MeSH------------
	--claims = entity['claims']['P672'] --MeSH Code
	--if claims then
	--	for  _,claim in pairs( claims ) do
	--		tmpValue[x] = claim.mainsnak.datavalue.value
	--		x = x + 1
	--	end
	--	p.setdata(itemId, "MeSH", tmpValue)
	--end
	--
	--tmpValue = {} --initialize
	--x = 1 --initialize
	--claims = nil --initialize
	-- ★★★★ End source specific code ★★★★ --
	
	---------------------------------------------------------------		
	-- End collecting data
	---------------------------------------------------------------
	
	--mw.logObject(datas[itemId])
	---------------------------------------------------------------		
	-- Start collecting childrens
	---------------------------------------------------------------
	claims = entity['claims'][property]
	
	--local claims = wikidata.getClaims(query) 
	if not claims then
		return {}
	end
	
	local has_source
	
	if sources then -- If input setting about "sources" exists
		has_source = false -- default
	else
		has_source = true -- when setting doesn't requires sources, assuming as if all cliams are sourced
	end
	
	for  _,claim in pairs( claims ) do
		if sources then -- If input setting about "sources" exists
			has_source = false -- reset
			
			-- ★★★★ Start source specific code ★★★★ --
			if claim['references'] and claim['references'][1]['snaks']['P248'] then -- If P248 ("stated in") claim exists
				if claim['references'][1]['snaks']['P248'][1]['datavalue']['value']['id'] == 'Q286567' then -- If it is TA98 ('Q286567')
					has_source = true --If the claim is sourced by Terminologia Anatomica, then "has_source" is true
					p.setdata('Q' .. claim.mainsnak.datavalue.value['numeric-id'], 'sourcedBy', 'TA98')-- set datas table, like datas[Q1234]['sourceType'] = 'TA98'
				end
			end
			-- ★★★★ End source specific code ★★★★ --
		end
		
		--Adding item only when the claim is sourced by specific literature (e.g. Terminologia Anatomica)
		--or adding all item if input setting doesn't need source
		if has_source then 
			local value = claim.mainsnak.datavalue.value
			local nextitem = 'Q' .. value['numeric-id']

			childrens[x] = nextitem
			x = x + 1
		end
	end
	--mw.logObject(datas[itemId])
	--mw.log("childrens are : ")
	--mw.logObject(childrens)
	return childrens
	---------------------------------------------------------------		
	-- End collecting childrens
	---------------------------------------------------------------
end
		
-- generator, see http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators for lua doc
-- definition a function that when called several times will generate a sequence of strings
 
-- gensymbols = create_gensymbols("ABC")
-- gensymbols() == "A" 
-- gensymbols() == "B" g
-- gensymbols() == "C" 
-- gensymbols() == "AA" '
-- gensymbols() == "BABBA"
-- ...
function p.gensymbols(chars)
	local i = 0
	local charset = chars
 
	local generator = function ()
		local symbol = ""
		local rest
 
		rest = i
 
		repeat
			local j 
			j = rest % string.len(charset)
			rest = math.floor(rest / string.len(charset))
			symbol = symbol .. string.sub(charset, j+1, j+1)
		until rest <= 0
		i = i + 1
		return symbol
	end
	return generator
end

-------------------------------------------------------------------------------------------	
-- Start definition of itemOutput function which returns text like '[[' .. link .. '|' .. label .. ']]'
-------------------------------------------------------------------------------------------	
function p.itemOutput(item, lang1, lang2, datas, setdata, getdata, refStyle, pencil)
		
	local globalSiteId_1 = lang1 .. 'wiki'
	local globalSiteId_2 = lang2 .. 'wiki'
	--local lang_2 = 'en'
	--local langWiki = '//'..lang..'.wikipedia.org' -- if lang is 'fr', then this is '//fr.wikipedia.org' used to compare with mw.site.server
	
	--local currentEntity = mw.wikibase.getEntityObject( item )
	local currentLabel
	local currentLabel_temp
	
	local content
	local refTable = {}
	local referenceText = ''
	local pencilText
	if pencil == 'none' then 
		pencilText = ''
	else --(P527 "has part" or P361 "is part of")
		pencilText = '[[File:Blue pencil.svg|frameless|text-top|10px|link=https://www.wikidata.org/wiki/' .. item .. '#P527]]'
	end
	
	refStyle = 'asdasdsa'
	--mw.log('refStyle is: ' .. refStyle)
	
	if refStyle and refStyle ~= 'none' then
	-- ★★★★ Start source specific code ★★★★ --
		if p.getdata(item, "sourcedBy", nil ) and p.getdata(item, "sourcedBy", nil )  == 'TA98' then
			--mw.log('datas[] is: ')
			--mw.logObject(datas)
			mw.log('<br/>')
			mw.log('<br/>')
			mw.log('item is: ')
			mw.logObject(item)
			mw.log('datas[item] is: ')
			mw.logObject(datas[item])
			mw.log('datas[item]["TA98"] is: ')
			mw.logObject(datas[item]["TA98"])
				refTable = p.getdata(item, "TA98", nil )
			mw.log('refTable is: ')
			mw.logObject(refTable)
			mw.log('<br/>')
			mw.log('<br/>')
			if refTable and refTable ~=  '' then
				for  i, TA98ID in pairs( refTable ) do
					refTable[i] = string.sub(refTable[i], 2) --Remove first character "A" from ID
					refTable[i] = '[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/' .. refTable[i] .. '%20Entity%20TA98%20EN.htm' .. ' ' .. 'A' .. refTable[i] ..']' -- Creating wikitext which links to TA98 official site
					--mw.log('refTable is :' .. tostring(table.concat(refTable)))
				end
				-- Converting multipul IDs into one line string, separated by comma, space ", "
				referenceText = tostring( table.concat( refTable, ', ' ) )
				referenceText = referenceText .. ' [[Terminologia Anatomica|TA98]], 1998'
				if refStyle == 'normal' then
					referenceText = tostring( mw.getCurrentFrame():extensionTag( 'ref', referenceText, {} ) ) 
				else
					referenceText = mw.getCurrentFrame():expandTemplate{ title = 'efn', args = {referenceText} }
				end
			else
				referenceText = ''
			end
		else
			referenceText = ''
		end
	-- ★★★★ End source specific code ★★★★ --
	end
	
	--mw.log('referenceText is :' .. referenceText)
	
	--Link creation priority. Followings is an example.
	--'ja'(Japanese) represents your own language, and 'en'(English) represents other language which is familiar with your readers.
	--This section would be better to customize for your own readers.
	
	--1. [[:ja:Sitelink(ja)|Sitelink(ja)]]           --If local sitelink data exists, use that.
	--2. [[:ja:Label(ja)|Label(ja)]](en link)        --If local sitelink data doesn't exist, but local label data exists, use that (with en link).
	--3. Sitelink(en) (en link)  --If both local language data doesn't exist, use sitelink to English Wikipedia
	--4. Label(en)(en link)        --If such data doesn't exist, use English label
	--5. Q123456                     --If all of such kind data doesn't exist, use bare item ID
	
	currentLabel = p.getdata(item, "sitelink_1", nil )
	-- wd._references( {item, 'P361'} ) 
	if currentLabel ~= nil then
		content = '[[:' .. lang1 .. ':' .. currentLabel .. '|' .. currentLabel .. ']]'
		content = content .. ' ' .. referenceText
		content = content .. ' ' .. pencilText 
		return content ----1. [[:ja:Sitelink(ja)|Sitelink(ja)]] style 
	else
		currentLabel = p.getdata(item, "label_1", nil )
		if currentLabel ~= nil then
			content = '[[:' .. lang1 .. ':' .. currentLabel .. '|' .. currentLabel .. ']]'
			currentLabel_temp = p.getdata(item, "sitelink_2", nil )
			if currentLabel_temp ~= nil then
				content = content .. ' ([[:' .. lang2 .. ':' .. currentLabel_temp .. '|' .. lang2 .. ']])' ----adding (en) link
			end
			content = content .. ' ' .. referenceText
			content = content .. ' ' .. pencilText 
			return content ----2. [[:ja:Label(ja)|Label(ja)]](en link) style
		else
			currentLabel = p.getdata(item, "sitelink_2", nil )
			if currentLabel ~= nil then
				content = currentLabel
				content = content .. ' ([[:' .. lang2 .. ':' .. currentLabel .. '|' .. lang2 .. ']])' ----adding (en) link
				content = content .. ' ' .. referenceText
				content = content .. ' ' .. pencilText 
				return content ----3. Sitelink(en) (en link) style
			else
				currentLabel = p.getdata(item, "label_2", nil )
				if currentLabel ~= nil then
					content = currentLabel
					content = content .. ' ([[:' .. lang2 .. ':' .. currentLabel .. '|' .. lang2 .. ']])' ----adding (en) link
					content = content .. ' ' .. referenceText
					content = content .. ' ' .. pencilText 
					return content ----4. Label(en)(en link) style
				else
					content = item
					content = content .. ' ' .. referenceText
					content = content .. ' ' .. pencilText 
					return content --5. Q123456 style
				end
			end
		end
	end
end
-------------------------------------------------------------------------------------------	
-- End definition of itemOutput function which returns text like '[[' .. link .. '|' .. label .. ']]'
-------------------------------------------------------------------------------------------	

function p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )
-------------------------------------------------------------------------------------------	
-- Start preparation of functions
-------------------------------------------------------------------------------------------	

	

		----- topological sort and meta data of the DAG ( https://en.wikipedia.org/wiki/Topological_sorting )
		
	--This firstPass() function is called only once, at first
	local function firstPass( query, firstItemId, item_repr )
	
	
	    local content = p.itemOutput(firstItemId, lang1, lang2, datas, setdata, getdata, refStyle, pencil)
 
	    local opened = 1 -- This means the downstream branch of the item is under processing.
						 -- If encountering opened item through the processing, it indicates that infinite loop exists.   
						 
	    local closed = 2 -- This means the processing of all downstream branches of the item reached to its end.
						 -- The downest item is showed with " ? " in tree.
						 
	    local incomplete = 3 -- This means the processing of all downstream branches of the item reached to its end.
							 -- If more downstream branches exist but not processed (because of limitation from maxdepth),
							 -- then the downest item showed with "…" in tree.
							 
	    local marks = {} --opened(1) or closed(2) or incomplete(3) for each QID (e.g. marks[Q12234234] -> closed)
		
 
	    --local datas = {} --datas[QID]["nparents"], datas[QID]["symbol"], datas[QID]["looped"], datas[QID]["rank"], datas[QID]["status"]
						--datas[QID]["nparents"] represents "number of parents" of certain item. This is normally 1.
						--If this is 2 or more, it means that the exact same item appears several times in defferent branches of the tree repeatedly.
						
		local childrens = {} -- for example, childrens[Q1234] contains data like {Q123123, Q3984198237, Q1874138746}, three childrens of Q1234
 		
		--while there are unmarked nodes do
		--    select an unmarked node n
		--    visit(n) 
		--function visit(node n)
		--    if n has a temporary mark then stop (not a DAG)
		--    if n is not marked (i.e. has not been visited yet) then
		--        mark n temporarily
		--        for each node m with an edge from n to m do
		--            visit(m)
		--        mark n permanently
		--        add n to head of L
 
		-- this function 
		-- * visits and builds the tree, DAG or graph,
		-- * in the same pass, computes a topological ordering for the DAG
		-- * annotates the nodes with informations
	    function visit(n, depth, rank)
	    	--mw.log("depth is " .. depth ..  ">=" .. "maxdepth is " .. maxdepth) 
 
			--mw.log(n .. ": mark is " .. tostring(marks[n]))
			if marks[n] == opened then 
				p.setdata(n, "status", "loop")
				p.setdata(n, "rank", rank)
				return rank
			elseif marks[n] ~= closed then
				marks[n] = opened
 
				childrens[n] = p.children( query, n, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2)
				for _, node in ipairs(childrens[n]) do
					p.setdata(node, "nparents", 
						p.getdata(node, "nparents", 0) + 1
					) 
					--mw.log(node .. ": nparents is " .. tostring(p.getdata(node, "nparents", "aaa")))
					if depth <= maxdepth then
						rank = visit(node, depth + 1, rank + 1)
					end
				end
 
				if depth <= maxdepth then
					if p.getdata(n, "status", "complete") ~= "loop" then
						p.setdata(n, "status", "complete")
					end
					marks[n] = closed
				else
					p.setdata(n, "status", "incomplete")
					marks[n] = incomplete
				end
 
				p.setdata(n, "rank", rank)
			end
			return rank + 1
		end
		p.setdata(firstItemId, "nparents", 0)
		visit(firstItemId, 1, 0)
 
		return datas, childrens
	end

	local langobj = mw.language.new(lang1)
	-- link inside tree 
	local function formatSymbol(prefix)
		return '<span class="Unicode"><small>(' .. prefix .. ")</small></span>"
	end
	local function genAnchor(id)
		return '<span id="' .. firstItemId .. id .. '"></span>' 
	end
	local function anchorLink(id, content)
		return "[[#" .. firstItemId .. id .. "|" .. content .. "]]"
	end
	local function fmtTreeLinks(content)
		return mw.text.tag("sup", {}, content)
	end
 
	local function renderTree( itemId, datas, children, alreadyOuted, gs )
		local content = p.itemOutput( itemId, lang1, lang2, datas, setdata, getdata, refStyle, pencil)
		local state
 
		if datas[itemId]["status"] ~= nil then
			state = datas[itemId]["status"] 
		end
		--mw.log(itemId .. ": status is " .. state )
 
		if 	datas[itemId] ["nparents"] > 1 and datas[itemId]["symbol"] == nil then
			p.setdata(itemId, "symbol", gs() )
		end
		-- prevent infinite loops in non DAGs
		if state == "loop" then
			if datas[itemId]["looped"] == nil then
				datas[itemId]["looped"] = "treated"
				content =  fmtTreeLinks(" ∞") .. " " .. content .. genAnchor(itemId)
			else
				return content ..  fmtTreeLinks("∞" ..	" ↑ " .. anchorLink(itemId, formatSymbol( datas[itemId]["symbol"] )))
			end
		elseif state == "incomplete" or state == "unvisited" then
			content = content .. " " .. fmtTreeLinks("…")
			return content
		end
 
		-- has no chilren ? display as leaf
		if children[itemId] ~= nil and table.getn(children[itemId]) == 0 then
			--return " " .. content .. " ? " -- would be great to use "?b, but font problem
			return " " .. content
		end
 
 
		datas[itemId] ["nparents"] = datas[itemId]["nparents"] - 1
 
	    local parts = {}
 
		-- sort children topologycally
		if alreadyOuted[itemId] == nil then
 
 			local langobj = mw.language.new(lang1)
		    local prefix = " "
		    if datas[itemId] ["nparents"] > 0 then
		    	local arrow = langobj:getArrow()
		    	prefix = fmtTreeLinks(genAnchor(itemId) .. " " .. arrow .. " " .. formatSymbol(datas[itemId]["symbol"]))
	    	end
				
			order = children[itemId]
	    	table.sort(order, function (a, b) return datas[a]["rank"] < datas[b]["rank"] end )
 
		    for i, childId in ipairs(order) do
		        table.insert( parts, renderTree( childId, datas, children, alreadyOuted, gs ) )
		    end
	 
			if direction == "child" or direction == "brother" then 
				local l = table.maxn( parts )
				for i = 1,(l - 1) do
					parts[i] = mw.text.tag( 'li', {}, parts[i] )
				end
				parts[l] = mw.text.tag( 'li', { class = 'lastline' }, parts[l] )
				alreadyOuted[itemId] = prefix .. " " .. content  .. mw.text.tag( 'ul', {}, table.concat( parts ) )
			elseif direction == "parent" then 				
				local texttmp
				local matchCount = 0
				--mw.log("parts is: ")
				--mw.logObject(parts)
				texttmp = mw.text.tag( 'ul', {}, mw.text.tag( 'li', { class = 'lastline' }, content ) )
				
				alreadyOuted[itemId], matchCount = string.gsub(table.concat( parts ), "(</li></ul>)", texttmp .. "%1", 1)
				--mw.log('matchCount is:')
				--mw.log(matchCount)
				if matchCount == 0 then
					alreadyOuted[itemId] = prefix .. " " .. string.gsub(table.concat( parts ), "$", texttmp, 1)
				end
				alreadyOuted[itemId] = prefix .. " " .. alreadyOuted[itemId]
				--mw.log("alreadyOuted[itemId] is: ")
				--mw.logObject(alreadyOuted[itemId])
			end
			
		end
 
		if datas[itemId] ["nparents"] <= 0 or state == "loop" then
			return alreadyOuted[itemId]
		else 
			return content .. " " .. fmtTreeLinks(anchorLink(itemId, formatSymbol(datas[itemId]["symbol"])) .. " " .. langobj:getArrow(forward))
		end
	end
-------------------------------------------------------------------------------------------	
-- End preparation of functions
-------------------------------------------------------------------------------------------	
 
-------------------------
-- Start creating tree --
-------------------------
	-- gen = p.gensymbols("??")
	-- gen = p.gensymbols("12")
	-- gen = p.gensymbols("★☆?")
	-- These symbols are used as link button for "jumping" from certain branch to other branch.
	-- "jumping" functionality is used when same item appears multipul times in one tree.
	local gen = p.gensymbols("@*#") 
 
	local children 
	datas, children = firstPass( query, firstItemId, gen)
	-- alreadyOuted is the table which contains html code for each QID.
	-- For example, like this... alreadyOuted[Q1234] = "<li>[[Human body]]</li>"
	-- Actually items which located more nearer to the root (to say, FirstItemId) contain html codes of their branchs within it. 
	-- For example, like this... alreadyOuted[Q1234] = "<li>[[Human body]] <ul><li>[[Nervous system]]</li></ul></li>"
	-- This is done by recursive calling of renderTree() function inside the renderTree() function.
	local alreadyOuted = {} 
	rendering = {} -- What does this do?
	return  renderTree( firstItemId, datas, children, alreadyOuted, gen)
-----------------------
-- End creating tree --
-----------------------
end

function p.main( frame )
------------------------------------
-- Start adjustments of arguments 
------------------------------------
    local frame = frame:getParent()
    local query = frame.args
	local sources
	local refStyle -- Option for refrence style. "none" or "normal" or other
	local pencil -- if pencil == "none", wikidata-linked pencil image is not shown 
    local maxdepth
    local lang1
    local lang2
	local firstItemId = query.items
	
	if firstItemId == nil or firstItemId == '' then
		--If QID is not defined, using the QID of the item which is connected to the current page.
		firstItemId = mw.wikibase.getEntityIdForCurrentPage() 
		if not firstItemId then
			return 'No items passed as parameter' --If there is no connected wikidata page, abort.
		end
    end
    --local firstItemsId = mw.text.split( query.items, ' ' )
    --if firstItemsId[1] == nil or firstItemsId[1] == '' or table.maxn( firstItemsId ) == 0 then
	--	--If QID is not defined, using the QID of the item which is connected to the current page.
	--	firstItemsId[1] = mw.wikibase.getEntityIdForCurrentPage() 
	--	if not firstItemsId[1] then
	--		return 'No items passed as parameter' --If there is no connected wikidata page, abort.
	--	end
    --end
	
	--for i, item in pairs( firstItemsId ) do
	if tonumber(firstItemId) then --legacy format
		firstItemId = 'Q'.. tostring(firstItemId)
	end
	--end
	
	if tonumber(query.childProperty) then --legacy format
		query.childProperty = 'P'.. tostring(query.childProperty)
	end
	
	if tonumber(query.parentProperty) then --legacy format
		query.parentProperty = 'P'.. tostring(query.parentProperty)
	end
	
	if query.childDepth and query.childDepth ~= '' then
		query.childDepth = tonumber( query.childDepth )
	else
		query.childDepth = 4
	end
	
	if query.parentDepth and query.parentDepth ~= '' then
		query.parentDepth = tonumber( query.parentDepth )
	else
		query.parentDepth = 2
	end
	
	if query.brotherDepth and query.brotherDepth ~= '' then
		query.brotherDepth = tonumber( query.brotherDepth )
	else
		query.brotherDepth = 0
	end
	
	if query.sources and query.sources ~= '' then
		sources = mw.text.split( query.sources, ' ' )
	else
		sources = nil
	end
	
	if query.refStyle and query.refStyle ~= '' then
		if query.refStyle == 'none' then
			refStyle = 'none'
		elseif query.refStyle == 'normal' then
			refStyle = 'normal'
		else
			refStyle = mw.text.split( query.sources, ' ' )
		end
	else
		refStyle = 'normal'
	end
	
	if query.pencil and query.pencil ~= '' then
		pencil = tostring(query.pencil)
	else
		pencil = nil
	end
	
	if query.lang1 and query.lang1 ~= '' then
		lang1 = query.lang1
	else
		lang1 = mw.language.getContentLanguage().code
	end
	
	if query.lang2 and query.lang2 ~= '' then
		lang2 = query.lang2
	else
		lang2 = 'en'
	end
	
----------------------------------
-- End adjustments of arguments 
----------------------------------

-------------------------
-- Start creating tree --
-------------------------
    local content = ''
    local parent_content = nil
    local child_content = nil
	local brother_content = nil
	local direction -- tree searching direction. "child", "parent" or "brother"
	local property -- property used to search items. For example "P527" (has part) or "P361" (part of)
	
	--------------------------------------------------
	--Creating tree for parent side (upstream side)
	--------------------------------------------------
	if query.parentProperty and query.parentProperty ~= '' then
		parent_content = ''
		direction = "parent"
		property = query.parentProperty
		maxdepth = query.parentDepth
		--for _, item in pairs( firstItemsId ) do
		   parent_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
		--end
	end
	
	--------------------------------------------------
	--Creating tree for brother side (substream side)
	--------------------------------------------------
	-- Brother is, as definition, "parent's child". So both query.parentProperty and query.childProperty is needed.
	if query.brotherDepth and query.brotherDepth >= 0 and query.parentProperty and query.parentProperty ~= '' and query.parentDepth >= 1 and query.childProperty and query.childProperty ~= '' then
	
		brother_content = ''
		direction = "brother"
		maxdepth = query.brotherDepth
		property = query.parentProperty
		
		-- Get parent QID
		local parents = p.children(query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )
		--local currentEntity = mw.wikibase.getEntityObject( firstItemId )
		--local claims = currentEntity['claims'][query.parentProperty]
		--if claims then -- If firstItemId has parent
		--	local x = 1
		--	local parents = {}
		--	for  _,claim in pairs( claims ) do -- Check all parent claims and get QID from there. And stored it into parents[] table.
		--			local value = claim.mainsnak.datavalue.value
		--			local nextitem = 'Q' .. value['numeric-id']

		--			parents[x] = nextitem
		--			x = x + 1
		--	end
			
			local currentEntity
			local claims
			local x = 1
			local brothers, brothersPart = {}
			property = query.childProperty
			mw.log("parents is :")
			mw.logObject(parents)
			-- Get brother QID
			for  _,parent in pairs( parents ) do
				
				brothersPart = p.children(query, parent, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 )
				mw.log("brothersPart is :")
				mw.logObject(brothersPart)
				--currentEntity = mw.wikibase.getEntityObject( parent )
				--claims = currentEntity['claims'][query.childProperty] -- Get child property in parent entity. Parent's child is brother.
				for  _,bro in pairs( brothersPart ) do -- Check all  child claims and get QID from there. And stored it into brothers[] table.
					--local value = claim.mainsnak.datavalue.value
					--local nextitem = 'Q' .. value['numeric-id']
					
					if bro ~= firstItemId then -- One of parent's child is ownself. It is not brother. So exclude that.
						brothers[x] = bro
						x = x + 1
					end
				end
			end
			
			mw.log("brothers is :")
			mw.logObject(brothers)
			-- Creating trees for brother
			local l = table.maxn( brothers )
			if l >= 1 then
				for i = 1,(l - 1) do
					brother_content = brother_content .. mw.text.tag( 'li', {}, p.outputTree( query, brothers[i], property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
				end
				brother_content = brother_content .. mw.text.tag( 'li', {}, p.outputTree( query, brothers[l], property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
			end
				
		--end
	end
	
	--------------------------------------------------
	--Creating tree for child side (downstream side)
	--------------------------------------------------
	if query.childProperty and query.childProperty ~= '' then
		child_content = ''
		direction = "child"
		property = query.childProperty
		maxdepth = query.childDepth
		if brother_content and brother_content ~= '' then
		   child_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
		else
		   child_content = mw.text.tag( 'li', { class = 'lastline' }, p.outputTree( query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2 ) )
		end
	end
	
	mw.log("parent_content is: ")
	mw.logObject(parent_content)
	mw.log("")
	mw.log("")
	mw.log("child_content is: ")
	mw.logObject(child_content)
	mw.log("")
	mw.log("")
	mw.log("brother_content is: ")
	mw.logObject(brother_content)
	mw.log("")
	mw.log("")
	
	--------------------------------------------------
	-- Combine parent and child (and brother) trees 
	--------------------------------------------------
	if parent_content and parent_content ~= '' and child_content and child_content ~= '' then
		-- Append child-tree to parent-tree.
		-- What is doing here is only replacing the last item (final '<li class=\"lastline\">...</li>') in "parent_content", by "child_content"
		-- But because of Lua regex function is not high functioning, bit complicated process is done here.
		local a = 0
		local b = 0
		
		-- Finding start position of final item by searching <li class=\"lastline\"> from the left to the right through text. 
		while string.find(parent_content, '<li class=\"lastline\">', b) do
			mw.log(a .. ' ' .. b)
			a, b = string.find(parent_content, '<li class=\"lastline\">', b)
		end
		mw.log(a .. ' ' .. b)
		
		local c = 0
		local d = 0
		-- Finding end position of final item by searching </ul></li>. 
		c, d = string.find(parent_content, '</ul></li>.*$') 
		--mw.log(c .. ' ' .. d)
		
		-- If c, d == nil, it means "parent_content" is one level, is not multipul level tree.
		-- In other words, it contains only one item which is correponding to "firstItemId".
		-- In such case, ignore "parent_content".
		-- Ignore "brother_content" also. Because if no parent known, no brother known.
		if c ~= nil and d ~= nil then 
			-- Replacing final item of "parent_content" by "child_content".
			-- If "brother_content" exists, inserts it just after the "child_content".
			if brother_content and brother_content ~= '' then
				content = string.sub(parent_content, 1, a - 1) .. brother_content .. child_content .. string.sub(parent_content, c)
			else
				content = string.sub(parent_content, 1, a - 1) .. child_content .. string.sub(parent_content, c)
			end
		else
			content = child_content
		end
	end
	
	mw.log("content after is: ")
	mw.logObject(content)
	
	--------------------------------------------------
	-- Final formatting and adding {{reflist}} like template at the bottom 
	--------------------------------------------------
	if parent_content or child_content then
		res =  mw.text.tag( 'div', { class = 'treeview' }, mw.text.tag( 'ul', {}, content ) )
		
		if sources then -- if sources are shown after each items like [1], then put {{reflist}} like template at the bottom 
			res = res .. frame:expandTemplate{ title = 'Hidden', args = { 'Sources', frame:expandTemplate{ title = 'notelist', args = {} } } }
		end
	end
-----------------------
-- End creating tree --
-----------------------
    return res
end

return p