local p =
datas =
-- These setdata and getdata functions are used to access (read and write) to "datas" tablefunction p.setdata(n, key, val) if datas[n]
function p.getdata(n, key, defval) if datas[n]
nil then return defval else return datas[n][key] endend
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 -------------------------------------------------------------------------------- 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']
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
"B" g-- gensymbols
"AA" '-- gensymbols
------------------------------------------------------------------------------------------- -- Start definition of itemOutput function which returns text like '' .. 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
'TA98' then --mw.log('datas[] is: ') --mw.logObject(datas) mw.log('
') mw.log('
') 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('
') mw.log('
') 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] = '.. 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 .. ' TA98, 1998' if refStyle
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, 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]
local langobj = mw.language.new(lang1) -- link inside tree local function formatSymbol(prefix) return '
(' .. prefix .. ")" end local function genAnchor(id) return '' end local function anchorLink(id, content) return "" .. 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"]"loop" then if datas[itemId]["looped"]
"incomplete" or state
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]
"child" or direction
"parent" then local texttmp local matchCount = 0 --mw.log("parts is: ") --mw.logObject(parts) texttmp = mw.text.tag('ul',, mw.text.tag('li',, content)) alreadyOuted[itemId], matchCount = string.gsub(table.concat(parts), "()", texttmp .. "%1", 1) --mw.log('matchCount is:') --mw.log(matchCount) if matchCount
"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] = "
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
nil or firstItemId
nil or firstItemsId[1]
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
'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',, 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',, p.outputTree(query, firstItemId, property, direction, maxdepth, sources, refStyle, pencil, lang1, lang2)) else child_content = mw.text.tag('li',, 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 '
return p