Jump to content

Module:Sandbox/Innesw/test

From Wikipedia, the free encyclopedia

This is an old revision of this page, as edited by Innesw (talk | contribs) at 10:22, 13 November 2024 (Fixes in constructing split claims). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

---------------- Defining variables--------------------
local Pop_P = "P1082"          -- population property
local Applies_P = "P518"       -- applies to part property
local Pointin_P = "P585"       -- point in time property
local DetMeth_P = "P459"       -- determination method property
local RefURL_P = "P854"        -- reference URL
local RefTitle_P = "P1476"     -- reference title
local RefPubIn_P = "P1433"     -- reference published in property
local DatePub_P = "P577"       -- date of publication property
local Publisher_P = "P123"     -- publisher property
local Retrieved_P = "P813"     -- retrieved property
local Instof_P = "P31"         -- instance of property
local ShortN_P = "P1813"       -- short name property

local CensusAU_I = "Q5058971"   -- Australian census item
local SAL_I = "Q33112019"       -- state suburb item (includes SSC and SAL)
--local GCCSA_I = "Q112762887"  -- Greater Capital City Statistical Area item
local LGA_I = "Q33127844"       -- Local Government Area item
local UCL_I = "Q33127891"       -- Urban Centre or Locality
--local SA2_I = "Q33128776"     -- SA2
--local SA3_I = "Q118313924"    -- SA3
local ILOC_I = "Q112729549"     -- Indigenous Location

local item = nil

local p = {}

--------------- Function LastURLSection returns last section of a url, ie: the text after the last '/' ----------------------

local function LastURLSection(url)
    local pos = 1
    local f
    while true do
        f = string.find(url, '/', pos, true)
        if (f == nil) then
            break
        else
            pos = f + 1
        end
    end
    return string.sub(url, pos)
end

--------------- Function SplitDoubleClaims returns the claims table with any claim with multiple points-in-time split into multiple claims. ----------------------

local function SplitDoubleClaims(claims)
    local oldclaims, newclaims = {}, {}
    for j, s in pairs(claims) do
        local npits = table.maxn(s.qualifiers[Pointin_P])
        if npits > 1 then
            -- we need to split this claim
            -- first, make copies of the claim, and the points-in-time
            local newc, pits = {}, {}
            for a, pit in pairs(s.qualifiers[Pointin_P]) do
            	table.insert(newc, mw.clone(s))
                table.insert(pits, mw.clone(pit))
            end
            -- for each point-in-time, only keep matching point-in-time, determination method and reference qualifiers from each copy
            for a, pit in pairs(pits) do
                local keeps = {}
                -- points in time
                for k, p in pairs(newc[a].qualifiers[Pointin_P]) do
                    if p.datavalue.value.time == pit.datavalue.value.time then
                        table.insert(keeps, mw.clone(p))
                    end
                end
                newc[a].qualifiers[Pointin_P] = {}
                for r, b in pairs(keeps) do
                    table.insert(newc[a].qualifiers[Pointin_P], b)
                end
                local year = string.sub(pit.datavalue.value.time, 2, 5)
                -- determination methods
                keeps = {}
                for k, p in pairs(newc[a].qualifiers[DetMeth_P]) do
                    local detmet = mw.wikibase.getEntity(p.datavalue.value.id)
                    if string.find(detmet.labels.en.value, year, 1, true) ~= nil then
                        table.insert(keeps, mw.clone(p))
                    end
                end
                newc[a].qualifiers[DetMeth_P] = {}
                for r, b in pairs(keeps) do
                    table.insert(newc[a].qualifiers[DetMeth_P], b)
                end
                -- references
                keeps = {}
                for k, p in pairs(newc[a].references) do
                    if p.snaks[RefURL_P] ~= nil and string.find(p.snaks[RefURL_P][1].datavalue.value, year, 1, true) ~= nil then
                        table.insert(keeps, mw.clone(p))
                    end
                end
                newc[a].references = {}
                for r, b in pairs(keeps) do
                    table.insert(newc[a].references, b)
                end
            end

            for k, p in pairs(newc) do
                table.insert(newclaims, p)
            end

            table.insert(oldclaims, j)
            
        end
    end
    
    -- remove the original splitable claims
    for k, p in pairs(oldclaims) do
        table.remove(claims, p)
    end
    -- and add the separate ones they were split into
    for k, p in pairs(newclaims) do
        table.insert(claims, p)
    end
end

--------------- Function IdForGeog returns the place ID for the specified geography abbreviation. Returns nil if abbreviation is blank. ----------------------

local function IdForGeog(geog)
    if geog == "ucl" then
        return UCL_I
    elseif geog == "sal" then
        return SAL_I -- includes SSC and SAL
    elseif geog == "lga" then
        return UCL_I
    elseif geog == "iloc" then
        return ILOC_I
    end
    return nil
end

--------------- Function GeogIdsForType returns a table of geography IDs that could be shown for the specified type. ----------------------

local function GeogIdsForType(type)
    type = string.lower(type)
    if type == "town" then
        return {UCL_I, ILOC_I, SAL_I}
    elseif type == "suburb" then
        return {SAL_I}
    elseif type == "city" then
        return {UCL_I}
    elseif type == "settlement" then
        return {SAL_I, ILOC_I}
    elseif type == "locality" then
        return {SAL_I, ILOC_I}
    elseif type == "townandlocality" then
        return {UCL_I, ILOC_I, SAL_I}
    elseif type == "lga" then
        return {LGA_I}
    elseif type == "region" then -- for now saying region uses LGA_I, but unclear what is most apprpriate ABS geography type. Can revise.
        return {LGA_I}
    end
end
--------------- Function GetRefsForClaim to check, collate and format all the reference components ----------------------

local function GetRefsForClaim(claim, defaulttitle)
    local refs = ""
    local r = 0
    for b, x in pairs(claim.references) do -- loop through all references in a claim
        -- each reference in the wikidata will produce a citation reference for the claim
        r = r + 1

        -- gather various values for potential later use
    
        local refurl = ""
        if claim.references[b].snaks[RefURL_P] ~= nil then -- if reference has a reference url, use it
            refurl = claim.references[b].snaks[RefURL_P][1].datavalue.value
        end

        local reftitle = defaulttitle -- default title is the Wikidata item title
        if claim.references[b].snaks[RefTitle_P] ~= nil then -- if reference has a title, use it
            reftitle = claim.references[b].snaks[RefTitle_P][1].datavalue.value.text
        end

        local detmet = mw.wikibase.getEntity(claim.qualifiers[DetMeth_P][1].datavalue.value.id) -- load the claim determination method item

        local pubinlabel = ""
        if claim.references[b].snaks[RefPubIn_P] ~= nil then -- if reference has a published in (it should for all references), use its item's label
            local pubin = mw.wikibase.getEntity(claim.references[b].snaks[RefPubIn_P][1].datavalue.value.id)
            pubinlabel = pubin.labels.en.value
        end

        local refwork = pubinlabel -- the default reference work for for non-census references, or fall-back for census references with missing parts

        local pubdate = ""
        if claim.references[b].snaks[DatePub_P] ~= nil then -- if reference has a date published, use it. This is the second-best option for the published date.
            pubdate = mw.language.getContentLanguage():formatDate('j F Y', claim.references[b].snaks[DatePub_P][1].datavalue.value.time)
        end
        
        if detmet.claims[Instof_P] ~=nil and detmet.claims[Instof_P][1].mainsnak.datavalue.value.id == CensusAU_I then
            -- if determination method is an instance of an australian census
            refwork = detmet.labels.en.value .. " " .. pubinlabel -- reference work is determination method label + published in
            if detmet.claims[DatePub_P] ~=nil then -- if determination method has a date published, use that as the date
                pubdate = mw.language.getContentLanguage():formatDate('j F Y', detmet.claims[DatePub_P][1].mainsnak.datavalue.value.time)
            end
        end

        local refpublisher = ""
        if detmet.claims[Publisher_P] ~= nil then -- if determination method has a publisher, use its item's label
            local publisheritem = mw.wikibase.getEntity(detmet.claims[Publisher_P][1].mainsnak.datavalue.value.id)
            refpublisher = publisheritem.labels.en.value
        end

        local refaccessdate = ""
        if claim.references[b].snaks[Retrieved_P] ~= nil then -- if reference has an access date, use it.
            refaccessdate = mw.language.getContentLanguage():formatDate('j F Y', claim.references[b].snaks[Retrieved_P][1].datavalue.value.time)
        end

        local appliespart = mw.wikibase.getEntity(claim.qualifiers[Applies_P][1].datavalue.value.id).labels.en.value -- the label of the item of the applies to part of the claim

        local year = string.sub(claim.qualifiers[Pointin_P][1].datavalue.value.time, 2, 5) -- the population point in time as a year string

        local reference

        if detmet.claims[Instof_P] ~=nil and detmet.claims[Instof_P][1].mainsnak.datavalue.value.id == CensusAU_I then
            -- if determination method is an instance of an australian census
            refwork = detmet.labels.en.value .. " " .. pubinlabel -- reference work is determination method label + published in
            
            -- the reference is built using the specific template for the census year, which ensures the link format is correct
            local geogid = LastURLSection(refurl) -- the id for the specific ABS reference is easiest to get from the ref URL. It may be the only place it is available.
            reference = mw.getCurrentFrame():expandTemplate{title = 'Census_' .. year .. '_AUS', args = {id = geogid, name = reftitle .. " (" .. appliespart .. ")", ["access-date"] = refaccessdate, quick = 'on'} }
        else
            -- use the provided reference url, and whatever other citation data is available

            local citewebargs = {
                url = refurl,
                title = reftitle .. " (" .. appliespart .. ")" ,
                date = pubdate,
                work = refwork,
                author = "[[" .. refpublisher .. "]]", -- author is used to match existing population references
                accessdate = refaccessdate
            }

            reference = mw.getCurrentFrame():expandTemplate{ title = 'cite web', args = citewebargs }
        end

        local wdeditpencil = mw.getCurrentFrame():expandTemplate{title = 'EditAtWikidata', args = {qid = item.id, pid = claim.id, nbsp = 1}} -- the Edit At Wikidata icon & link
        
        reference = reference .. wdeditpencil

         -- The name of the citation reference will be the same for each wikidata claim reference. This will allow references to the same data to be combined into a single citation reference.
        local refname = refwork .. "_" .. year .. "_" .. appliespart .. "_" .. reftitle
        if r > 1 then -- 2nd and later references in the same wikidata claim have their number appended, to keep them unique
            refname = refname .. "_" .. r
        end

        refs = refs .. mw.getCurrentFrame():extensionTag{ name = 'ref', content = reference, args = { name = refname} } -- accumulate the citation references
    end
    return refs
end

--------------- Function GetAbbrLabel gets the population geography abbreviation ---------------

local function GetAbbrLabel(returnclaim)
    local appliespartitem = mw.wikibase.getEntity(returnclaim.qualifiers[Applies_P][1].datavalue.value.id) -- load the applies to part item
    
    local abbrelabel = appliespartitem.labels.en.value -- the fall back value for the geography label if no abbreviation (short name) value exists in Wikidata item
    if appliespartitem.claims[ShortN_P] ~= nil then -- if a short name value exists, use it, with the full label as a tooltip
        abbrelabel = mw.getCurrentFrame():expandTemplate{title = 'Abbr', args = {appliespartitem.claims[ShortN_P][1].mainsnak.datavalue.value.text, appliespartitem.labels.en.value } }
    end
    return '[[Australian Bureau of Statistics#' .. string.gsub(appliespartitem.labels.en.value, ' ', '_') .. '|' .. abbrelabel .. ']]'
end

--------------- Function GetYearLink gets the Wikipedia article link for the population year ---------------

local function GetYearLink(returnclaim)
    local year = string.sub(returnclaim.qualifiers[Pointin_P][1].datavalue.value.time, 2, 5) -- the population point in time as a year string
    local yearreturn = year -- if no links to Wikipedia articles describing population determination method exist, default is year

    local detmetitem = mw.wikibase.getEntity(returnclaim.qualifiers[DetMeth_P][1].datavalue.value.id) -- load the claim determination method item

    if detmetitem.sitelinks ~=nil and detmetitem.sitelinks.enwiki ~=nil then -- if determination method item has an enwiki URL
        yearreturn = "[[" .. detmetitem.sitelinks.enwiki.title .. "|" .. year .. "]]" -- use that URL as the link for the year value
    elseif detmetitem.claims[Instof_P] ~=nil and detmetitem.claims[Instof_P][1].mainsnak.datavalue.value.id == CensusAU_I then -- if determination method is an instance of an australian census
        yearreturn = "[[Census_in_Australia#" .. year .. "|" .. year .. "]]" -- use the section of the Census in Australia article as the link for the year value
    end
    return yearreturn
end

---------------- Function HistoricPopulations returns a wikitable of all census population values for all geography types, or a specified one ---------------
-- parameters:
-- required: type=     the type value as for the Infobox
-- optional: wikidata= the wikidata item to be used instead of the one in the current page
-- optional: geog=     a single geography type to return pop values for. Valid are 'ucl', 'sal', 'lga', 'iloc'. If left blank, all geographies will be returned.

function p.HistoricPopulations( frame )
    if frame.args.wikidata ~= nil and frame.args.wikidata ~= "" then -- if there's a Wikidata item specified, use it
        item = mw.wikibase.getEntity(frame.args.wikidata)
    else
        item = mw.wikibase.getEntity() -- if there's a Wikidata item connected to the article it will find it here.
    end

    -- if there are no population claims in the item, return an empty string
    if not (item and item.claims and item.claims[Pop_P]) then
        return ""
    end

    -- Find claims with:
    -- (1) point in time is not nil
    -- (2) applies to part is not nil
    -- (3) determination method is not nil
    -- (4) References table is not empty
    -- (5) The determination method for the claim is an australian census

    local validpopclaims = {}
    local z = 0
    for j, s in pairs(item.claims[Pop_P]) do
        local isCensus = false
        if s.qualifiers[DetMeth_P] ~= nil then
            local detmetitem = mw.wikibase.getEntity(s.qualifiers[DetMeth_P][1].datavalue.value.id) -- load the claim determination method item
            local isCensus = (detmetitem.claims[Instof_P] ~=nil and detmetitem.claims[Instof_P][1].mainsnak.datavalue.value.id == CensusAU_I) -- is determination method an instance of an australian census?
        end
        if s.qualifiers ~= nil and
          s.qualifiers[Pointin_P] ~= nil and
          s.qualifiers[Applies_P] ~= nil and
          s.qualifiers[DetMeth_P] ~= nil and
          s.references ~= nil and
          isCensus then
            z = z + 1
            validpopclaims[z] = s -- add to valid claims table
        end
    end

    -- if there are no valid claims, return an empty string
    if #validpopclaims < 1 then
        return ""
    end

    SplitDoubleClaims(validpopclaims) -- any claims with multiple points-in-time are split into separate claims

    -- add to history table for all (or requested-geography-only) claims

    local showGeogIds = {}

    if frame.args.geog ~= nil and frame.args.geog ~= "" then -- if geog is specified, only claims for its id are returned
        showGeogIds[1] = IdForGeog(string.lower(frame.args.geog))
    else
        showGeogIds = GeogIdsForType(frame.args.type)
    end
    local showGeogIdsString = ',' .. table.concat(showGeogIds, ',') .. ','

    local geog = nil
    if frame.args.geog ~= nil and frame.args.geog ~= "" then
        geog = string.lower(frame.args.geog)
    end

    local oneplaceid = IdForGeog(geog)

    local history = {}
    local years = {}
    local glist = {}
    for i, q in pairs(validpopclaims) do
        local claimgeogid = q.qualifiers[Applies_P][1].datavalue.value.id -- the ID of the applies_to_part item in the claim
        if string.find(showGeogIdsString, ',' .. claimgeogid .. ',', 1, true) then -- the geography ID of the claim is in the list of IDs that could be shown for the type
            if (not oneplaceid) or (claimgeogid == oneplaceid) then -- if geog is not specified, or it is and the claim applies_to_part matches it
                local claimyear = string.sub(q.qualifiers[Pointin_P][1].datavalue.value.time, 2, 5) -- the population point in time as a year string
                if not history[claimyear] then
                    history[claimyear] = {year = claimyear, claim = {}}
                    table.insert(years, claimyear)
                end
                local refs = GetRefsForClaim(q, item.labels.en.value)
                history[claimyear].claim[claimgeogid] = mw.language.getContentLanguage():formatNum(tonumber(q.mainsnak.datavalue.value.amount)) .. refs
                glist[claimgeogid] = 1
            end
        end
    end
    
    -- sort the years table
    table.sort(years)
    
    local geogNames = {[UCL_I] = 'UCL', [SAL_I] = 'SAL', [ILOC_I] = 'ILOC', [LGA_I] = 'LGA'}

    -- build the wikidata table from the history table
    local wt = {}
    table.insert(wt, '{| class="wikitable"') -- start of table
    -- header row
    table.insert(wt, '\n|-\n!') -- empty top-left cell
    for g, l in pairs(showGeogIds) do
        if glist[l] then
            table.insert(wt, ' !! ' .. geogNames[l])
        end
    end
    -- data rows
    for k, v in ipairs(years) do
        table.insert(wt, '\n|-\n! | ' .. v) -- first column, contains years
        for g, l in pairs(showGeogIds) do
            if glist[l] then
                if not history[v].claim[l] then
                    table.insert(wt, '\n| ') -- empty table cell
                else
                    table.insert(wt, '\n| ' .. history[v].claim[l])
                end
            end
        end
    end
    table.insert(wt, '\n|}') -- end of table

    return table.concat(wt)
end

---------------- Function LatestPopulation returns the most recent population value for a specified geography ---------------
-- parameters:
-- required: geog=     a single geography type to return pop value for. Valid are 'ucl', 'sal', 'lga', 'iloc'.
-- optional: wikidata= the wikidata item to be used instead of the one in the current page
-- optional: year=     any value (except 'no') requests the year to be shown after the population figure
-- optional: punc=     any value will be inserted into the output before the reference number

function p.LatestPopulation( frame )
    if frame.args.geog == nil then
        return ""
    end

    local geogID = IdForGeog(string.lower(frame.args.geog))
    if geogID == nil then
        return ""
    end 

    if frame.args.wikidata ~= nil and frame.args.wikidata ~= "" then -- if there's a Wikidata item specified, use it
        item = mw.wikibase.getEntity(frame.args.wikidata)
    else
        item = mw.wikibase.getEntity() -- if there's a Wikidata item connected to the article it will find it here.
    end

    -- if there are no population claims in the item, return an empty string
    if not (item and item.claims and item.claims[Pop_P]) then
        return ""
    end

    ------------ PART 1: Find claims that meet mimimum criteria

    -- (1) point in time is not nil
    -- (2) applies to part is not nil
    -- (3) determination method is not nil
    -- (4) References table is not empty

    local validpopclaims = {}
    local z = 0
    for j, s in pairs(item.claims[Pop_P]) do
        if s.qualifiers ~= nil and
          s.qualifiers[Pointin_P]~= nil and
          s.qualifiers[Applies_P] ~= nil and
          s.qualifiers[DetMeth_P] ~= nil and
          s.references ~= nil
        then
            z = z + 1
            validpopclaims[z] = s -- add to valid claims table
        end
    end

    -- if there are no valid claims, return an empty string
    if #validpopclaims <1 then
        return ""
    end
    
    SplitDoubleClaims(validpopclaims) -- any claims with multiple points-in-time are split into separate claims

    --------------- PART 2: Find the latest claim for each geography found
    
    local latestclaim = {}
    
    for i, q in pairs(validpopclaims) do
        local oclaimdate = q.qualifiers[Pointin_P][1].datavalue.value.time
        local claimgeog = q.qualifiers[Applies_P][1].datavalue.value.id
        if  latestclaim[claimgeog] == nil
          or (latestclaim[claimgeog] ~= nil and oclaimdate >= latestclaim[claimgeog].qualifiers[Pointin_P][1].datavalue.value.time) then -- if the max date for a particular geography value is later than the previous latest, overwrite with the current latest claim
            latestclaim[claimgeog] = q
        end
    end

    --------------- PART 3: Compile the module output, using only latest claim for the specified geography

    local wikitext = ""
    if latestclaim[geogID] ~= nil then
        local yearlink = ""
        if (frame.args.year or '') ~= '' and frame.args.year:lower() ~= 'no' then -- year is only shown on request
            yearlink = " (" .. GetYearLink(latestclaim[geogID]) .. ")" 
        end
        local refs = GetRefsForClaim(latestclaim[geogID], item.labels.en.value) -- the references for the claim
        wikitext = mw.language.getContentLanguage():formatNum(tonumber(latestclaim[geogID].mainsnak.datavalue.value.amount)) .. yearlink .. (frame.args.punc or '') .. refs
    end

    local cat = ''
    if mw.title.getCurrentTitle().namespace == 0 then
        -- category not added except in article namespace
        cat = '[[Category:Australian place articles using Wikidata population values]]'
    end
    return  wikitext .. cat
end

---------------- Function ListForInfobox returns the most recent population values ---------------
-- parameters:
-- required: type=     the type value as for the Infobox
-- optional: wikidata= the wikidata item to be used instead of the one in the current page
-- optional: geog=     a single geography type to return pop values for. Valid are 'ucl', 'sal', 'lga', 'iloc'. If left blank, all geographies will be returned.

function p.ListForInfobox( frame )
    if frame.args.type == nil then
        return ""
    end

    local luaplacetype = ""

    local articleplacetype = string.lower(frame.args.type) -- for the place type supplied, change to a lower case string

    if articleplacetype == "town" -- Check for valid place types
      or articleplacetype == "suburb" 
      or articleplacetype == "city" 
      or articleplacetype == "settlement"
      or articleplacetype == "locality"
      or articleplacetype == "townlocality"
      or articleplacetype == "lga"
      or articleplacetype == "region"
    then
      -- OK to continue
    elseif articleplacetype == "cadastral"
      or articleplacetype == "protected" then
        -- these place types don't have ABS populations
        return ""
    else
        -- unrecognised type supplied
        return ""
    end

    if frame.args.wikidata ~= nil and frame.args.wikidata ~= "" then -- if there's a Wikidata item specified, use it
        item = mw.wikibase.getEntity(frame.args.wikidata)
    else
        item = mw.wikibase.getEntity() -- if there's a Wikidata item connected to the article it will find it here.
    end

    -- if there are no population claims in the item, return an empty string
    if not (item and item.claims and item.claims[Pop_P]) then
        return ""
    end
    
    ------------ PART 1: Find claims that meet mimimum criteria

    -- (1) point in time is not nil
    -- (2) applies to part is not nil
    -- (3) determination method is not nil
    -- (4) References table is not empty

    local validpopclaims = {}
    local z = 0
    for j, s in pairs(item.claims[Pop_P]) do
        if s.qualifiers ~= nil and
          s.qualifiers[Pointin_P]~= nil and
          s.qualifiers[Applies_P] ~= nil and
          s.qualifiers[DetMeth_P] ~= nil and
          s.references ~= nil
        then
            z = z + 1
            validpopclaims[z] = s -- add to valid claims table
        end
    end

    -- if there are no valid claims, return an empty string
    if #validpopclaims <1 then
        return ""
    end
    
    SplitDoubleClaims(validpopclaims) -- any claims with multiple points-in-time are split into separate claims

    --------------- PART 2: Find the latest claim for each geography found
    
    local latestclaim = {}
    
    for i, q in pairs(validpopclaims) do
        local oclaimdate = q.qualifiers[Pointin_P][1].datavalue.value.time
        local claimgeog = q.qualifiers[Applies_P][1].datavalue.value.id
        if  latestclaim[claimgeog] == nil
          or (latestclaim[claimgeog] ~= nil and oclaimdate >= latestclaim[claimgeog].qualifiers[Pointin_P][1].datavalue.value.time) then -- if the max date for a particular geography value is later than the previous latest, overwrite with the current latest claim
            latestclaim[claimgeog] = q
        end
    end

    --------------- PART 3: specify the geography types that can be returned for each place type

    local showGeogIds = {}

    if frame.args.geog ~= nil and frame.args.geog ~= "" then -- if geog is specified, only claims for its id are returned
        showGeogIds[1] = IdForGeog(string.lower(frame.args.geog))
    else
        showGeogIds = GeogIdsForType(articleplacetype)
    end

    --------------- PART 4: Compile the module output, using only latest claims in specified geographies

    local returnlist = {}

    for j, t in pairs(showGeogIds) do
        if latestclaim[t] ~= nil then
            local refs = GetRefsForClaim(latestclaim[t], item.labels.en.value) -- the references for the max date claim
            table.insert(returnlist, mw.language.getContentLanguage():formatNum(tonumber(latestclaim[t].mainsnak.datavalue.value.amount)) .. " (" .. GetAbbrLabel(latestclaim[t]) .. " " .. GetYearLink(latestclaim[t]) .. ")" .. refs)
        end
    end

    local wikitext = ""
    if #returnlist == 1 then
        -- if there is only one entry in returnlist, return it without a bullet point
        wikitext = returnlist[1]
    else
        -- if there are multiple entries in returnlist, return all the rows with new line and bullet points between them
        wikitext = "\n*" .. table.concat(returnlist, "\n*")
    end
    local cat = ''
    if mw.title.getCurrentTitle().namespace == 0 then
        -- category not added except in article namespace
        cat = '[[Category:Australian place articles using Wikidata population values]]'
    end
    return  wikitext .. cat
end

-- ######  this function is just for testing of the upgrade during development

function p.main()
	  local wdata = 'Q11568'
    return p.HistoricPopulations( { args = {type = 'town', wikidata = wdata} } )
      .. '\n\n' .. p.HistoricPopulations( { args = {type = 'town', wikidata = wdata, geog='sal'} } )
      .. p.LatestPopulation( { args = {geog = 'sal', wikidata = wdata} } ) .. '<br>'
      .. p.ListForInfobox( { args = {type = 'town', wikidata = wdata} } )
end

return p