File:Ambox warning blue construction.svg | This module is rated as pre-alpha. It is unfinished, and may or may not be in active development. It should not be used from article namespace pages. Modules remain pre-alpha until the original editor (or someone who takes one over if it is abandoned for some time) is satisfied with the basic structure. |
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)
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.
- Template:Done: Rendering parent-side tree (upstream toward human body)
- Need to process multiple-parents cases (bifurcated parent tree)
- 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.
- 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)
- Is it possible to make tree references more compact? Because tree references would be needed (WP:V), but actually not so interesting.
- Treat species general and human specific articles. Fro example, "Human brain" and "Brain", "Human head" and "Head".
- (Re)arrange some input options, especially source and appearance related options.
- 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
- Module:Sandbox/Was a bee/tree - Lua code which generates following trees
- User:Was a bee/Tree - wrapper template
- mw:Extension:Scribunto/Lua reference manual - Lua help
- mw:Extension:Wikibase Client/Lua - Lua help for Wikidata
- Tree technologies from discussion at Wikipedia:Village pump (technical)#Mirror_of_template:Tree_list.2Ffinal_branch (perm link)
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.
- Terminologia Anatomica (TA98) = Q286567
- Other possible candidates
- Terminologia Embryologica (TE)
- Terminologia Histologica (TH)
- FMA
- UBERON
- MeSH
- NeuroNames
- Gene Ontology
- .....
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
- Starting from Template:Wde
- Tracking through Template:Wdp for downward 10 depth
- Tracking through Template:Wdp for upward 5 depth
- Only showing data which is sourced by Template:Wde (excluding other data)
{{User:Was a bee/Tree|items=Q47273|childProperty=P527|childDepth=10|parentProperty=P361|parentDepth=5|sources=TA98}}
Tree based on data including unreferenced dataEdit
- Starting from Template:Wde
- Tracking through Template:Wdp for downward 10 depth
- Tracking through Template:Wdp for upward 5 depth
- Showing all data (including unreferenced data)
{{User:Was a bee/Tree|items=Q47273|childProperty=P527|childDepth=10|parentProperty=P361|parentDepth=5||sources=}}
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