Module:JSONPageList

Module documentation
-- Demo for T332484
require( 'strict' )
local r={}
--local lang="en"
local site="enwiki"
local siteprefix="en:"

local function get_link(qid, label)
    local sitelink=mw.wikibase.getSitelink(qid,site) 
    label=label or mw.wikibase.getLabel(qid)
    if sitelink then
        return '[[:'..siteprefix..sitelink..'|'..label..']]'
    else
        return '[[d:'..qid..'|'..label..']]'
    end
end

local function get_heading(field)
    if field:match(":(.*)$") then
        return field:match(":(.*)$")
    elseif field=='@link' then
        return 'item'
    elseif field:sub(1,1)=='$' then
        local entity_id=field:sub(2):match("([PQpq]%d+)$")
        return mw.wikibase.getLabel(entity_id)
    else
        return field
    end
end

local function get_cell(field, item, data_fields_map, raw)
    if field:match("(.*):.*$") then
        field=field:match("(.*):.*$")
    end
    local qid=item._qid
    if field=='@qid' then
        return qid or ''
    elseif field=='@link' then
        if qid and not item._page then
            return get_link(qid, item._name)
        else
            return '[[:'..siteprefix..(item._page or item._name)..'|'..item._name..']]'
        end
    elseif field=='@description' then
        return mw.wikibase.getDescription(qid)
    elseif field:sub(1,1)=='$' then
        local entity_path=field:sub(2)
        if entity_path:match("^([Pp]%d+)$") then
            return mw.getCurrentFrame():callParserFunction("#statements",{entity_path, from=qid})
        elseif entity_path:match("^([Pp]%d+)/([Qq]%d+)/([Pp]%d+)$") then
            local pid, qid2, pid2=entity_path:match("^([PQpq]%d+)/([PQpq]%d+)/([PQpq]%d+)$")
            local vals=mw.wikibase.getBestStatements(qid, pid)
            for _, v in ipairs(vals) do
                if v.mainsnak.datavalue.value and v.mainsnak.datavalue.value.id==qid2 and v.qualifiers and v.qualifiers[pid2] then
                    return mw.wikibase.formatValues(v.qualifiers[pid2])
                end
            end
        end
    else
        if item[field] then
            return tostring(item[field])
        elseif qid and data_fields_map[field] and data_fields_map[field].property then
            local vals=mw.wikibase.getBestStatements(qid, data_fields_map[field].property)
            if data_fields_map[field].enum_map and vals and vals[1] and vals[1].mainsnak.datavalue.value and data_fields_map[field].enum_map[vals[1].mainsnak.datavalue.value.id] then
                return tostring(data_fields_map[field].enum_map[vals[1].mainsnak.datavalue.value.id])
            end
        end
        if data_fields_map[field] and data_fields_map[field].default then
            return tostring(data_fields_map[field].default)
        end
    end
    return '...'
end

local function parse_params(pagename, result_fields, params)
    if type(pagename) == "table" then
        pagename, result_fields, params = pagename.args[1], pagename.args[2], pagename
    end
    if params:getParent() and params:getParent().args[1] then
        pagename, result_fields, params = params:getParent().args[1], params:getParent().args[2], params:getParent()
    end
    params=params.args
    return pagename, result_fields, params
end

local function get_content(pagename)
    local content
    if mw.title.new( pagename ).namespace==828 then
        content=require(pagename)
    else
        content=mw.title.new(pagename):getContent()
        content=mw.text.jsonDecode(content)
    end
    return content
end

local function set_site(content)
    site=content.site or site
    siteprefix=content.siteprefix or siteprefix
end


local function get_fields_and_items(content)
    local data_fields=content.fields or {}
    local data_fields_map={}
    for _, v in ipairs( data_fields ) do
        if type(v)=="table" then
            data_fields_map[v[1]]=v
        end
        if v.enum then
            v.enum_map={}
            for _, p in ipairs( v.enum ) do
                if type(p)=='table' then
                    v.enum_map[p[2]]=p[1]
                end
            end
        end
    end
    local items=content.items
    for i, v in ipairs( items ) do
        if type(v) == "string" then
            items[i]={_qid=v}
        end
    end
    return data_fields, data_fields_map, items
end

local function clear_csv(param_str)
    if not param_str then
        return {}
    end
    local param_val=mw.text.split(param_str,',')
    local result={}
    for _,v in ipairs(param_val) do
        if mw.text.trim(v)~='' then
            table.insert(result,mw.text.trim(v))
        end
    end
    return result
end

local function populate_result_fields(result_fields, data_fields)
    result_fields=clear_csv(result_fields)
    if #result_fields==0 then
        result_fields={'@link'}
        for _, v in ipairs( data_fields ) do
            table.insert(result_fields,v[1] .. (v.display_name and (":"..v.display_name) or ''))
        end
    end
    return result_fields
end

local function parse_data(pagename, result_fields, params)
    pagename, result_fields, params = parse_params(pagename, result_fields, params)
    local content=get_content(pagename)
    set_site(content)
    local data_fields, data_fields_map, items=get_fields_and_items(content)
    result_fields=populate_result_fields(result_fields, data_fields)

    -- filter
    local filter=clear_csv(params.filter)
    if #filter>0 then
        local filtered_items={}
        for _, v in ipairs(items) do
            local selected=true
            for _, v2 in ipairs(filter) do
                if v2:match("(.-)==(.*)") then
                    local l, r=v2:match("(.-)==(.*)")
                    if get_cell(l,v,data_fields_map)~=r then
                        selected=false
                        break
                    end
                elseif v2:match("(.-)!=(.*)") then
                    local l, r=v2:match("(.-)!=(.*)")
                    if get_cell(l,v,data_fields_map)==r then
                        selected=false
                        break
                    end
                elseif v2:match("(.-)>=(.*)") then
                    local l, r=v2:match("(.-)>=(.*)")
                    if get_cell(l,v,data_fields_map)<r then
                        selected=false
                        break
                    end
                elseif v2:match("(.-)<=(.*)") then
                    local l, r=v2:match("(.-)<=(.*)")
                    if get_cell(l,v,data_fields_map)>r then
                        selected=false
                        break
                    end
                elseif v2:match("(.-)>(.*)") then
                    local l, r=v2:match("(.-)>(.*)")
                    if get_cell(l,v,data_fields_map)<=r then
                        selected=false
                        break
                    end
                elseif v2:match("(.-)<(.*)") then
                    local l, r=v2:match("(.-)<(.*)")
                    if get_cell(l,v,data_fields_map)>=r then
                        selected=false
                        break
                    end
                end
            end
            if selected then
                table.insert(filtered_items,v)
            end
        end
        items=filtered_items
    end

    -- orderby

    local orderby=clear_csv(params.orderby)
    local function _o(i1, i2)
        for _, v in ipairs(orderby) do
            local v1=get_cell(v,i1,data_fields_map)
            local v2=get_cell(v,i2,data_fields_map)
            if v1<v2 then
                return true
            elseif v1>v2 then
                return false
            end
        end
        return false
    end
    if #orderby>0 then
        table.sort(items, _o)
    end

    -- groupby

    local groupby=clear_csv(params.groupby)
    local item_groups={}
    for _, v in ipairs( items ) do
        local cur_node=item_groups
        for _, v2 in ipairs(groupby) do
            local subnode_name=get_cell(v2,v,data_fields_map)
            if cur_node[subnode_name]==nil then
                if cur_node._subnodes==nil then
                    cur_node._subnodes={}
                end
                table.insert(cur_node._subnodes,subnode_name)
                cur_node[subnode_name]={}
            end
            cur_node=cur_node[subnode_name]
        end
        table.insert(cur_node,v)
    end
    return pagename, result_fields, params, content, data_fields, data_fields_map, item_groups
end

function r.table(pagename, result_fields, params)
    local content, data_fields, data_fields_map, item_groups
    pagename, result_fields, params, content, data_fields, data_fields_map, item_groups=parse_data(pagename, result_fields, params)
    local result=mw.html.create("table"):attr( 'class', 'wikitable' )
    local heading=result:tag("tr")
    for i, field in ipairs( result_fields ) do
        heading:tag("th"):wikitext(get_heading(field))
    end
    local function populate_table(node, level)
        for _, item in ipairs(node) do
            local row=result:tag("tr")
            for _, field in ipairs( result_fields ) do
                row:tag("td"):wikitext(get_cell(field,item,data_fields_map))
            end
        end
        if node._subnodes then
            for _, v in ipairs(node._subnodes) do
                result:tag("tr"):tag("td"):attr("colspan",#result_fields):wikitext('\n' .. ('='):rep(level) .. v .. ('='):rep(level) .. '\n')
                populate_table(node[v], level+1)
            end
        end
    end
    populate_table(item_groups, tonumber(params.level) or 2)
    return tostring( result )
end

function r.plainlist(pagename, result_fields, params)
    local content, data_fields, data_fields_map, item_groups
    pagename, result_fields, params, content, data_fields, data_fields_map, item_groups=parse_data(pagename, result_fields, params)
    local all_results={}
    local function populate_plainlist(node, level)
        local clear_node={}
        if node[1] then
            for _, v in ipairs(node) do
                table.insert(clear_node,get_cell("@link",v,data_fields_map))
            end
	    end
        table.insert(all_results,table.concat(clear_node,', '))
        if node._subnodes then
            for _, v in ipairs(node._subnodes) do
                table.insert(all_results,('='):rep(level) .. v .. ('='):rep(level))
                populate_plainlist(node[v], level+1)
            end
        end
    end
    populate_plainlist(item_groups, tonumber(params.level) or 2)
    return table.concat(all_results,'\n')
end

function r.navbox(pagename, result_fields, params)
    local content, data_fields, data_fields_map, item_groups
    pagename, result_fields, params, content, data_fields, data_fields_map, item_groups=parse_data(pagename, result_fields, params)
    local navbox_mod=require('Module:Navbox')
    local function populate_navbox(node, primary)
        local clear_node={}
        local result
        if node[1] then
            for _, v in ipairs(node) do
                table.insert(clear_node,'* '..get_cell("@link",v,data_fields_map)..'\n')
            end
            result=table.concat(clear_node)
	    end
        if node._subnodes then
        	result={list1=result}
            local n=result.list1 and 1 or 0
            for _, v in ipairs(node._subnodes) do
                n=n+1
                result['group'..n]=v
                local sub_result=populate_navbox(node[v])
                if type(sub_result)=='table' then
                    sub_result[1]='child'
                    result['list'..n]=navbox_mod._navbox(sub_result)
                else
                    result['list'..n]=sub_result
                end
            end
            return result
        else
            return result or ''
        end
    end
    local result=populate_navbox(item_groups)
    if type(result)=='string' then
        result={list1=result}
    end
    result.title='Navbox'
    result.listclass='hlist'
    return navbox_mod._navbox(result)
end
return r