Module:ProjectCounts: Difference between revisions

Al Piskun (talk | contribs)
Created page with "-- Counts data rows in the wikitable(s) inside one named section of a page. -- Usage: {{#invoke:ProjectCounts|count|Active BOINC Projects|page=BOINC projects}} local p = {} local function esc(s) return (s:gsub('(%W)', '%%%1')) end function p.count(frame) local section = mw.text.trim(frame.args[1] or '') local title = mw.title.new(frame.args.page or 'BOINC projects') if not title then return 0 end local content = title:getContent() if not content the..."
 
Al Piskun (talk | contribs)
No edit summary
 
(One intermediate revision by the same user not shown)
Line 1: Line 1:
-- Counts data rows in the wikitable(s) inside one named section of a page.
-- Module:ProjectCounts
-- Usage: {{#invoke:ProjectCounts|count|Active BOINC Projects|page=BOINC projects}}
-- Counts BOINC project table rows and related figures for the wiki.
--
-- {{#invoke:ProjectCounts|count|Section name|page=Page title}}
--    Data rows in the wikitable(s) of one section.
-- {{#invoke:ProjectCounts|total|Section A|Section B|...|page=Page title}}
--    Sum of data rows across several sections of one page.
-- {{#invoke:ProjectCounts|documented|Local BOINC project wiki pages|page=Main Page}}
--    Distinct existing mainspace articles linked in a section.
-- {{#invoke:ProjectCounts|recordings|Screensaver Gallery|page=BOINC project screensavers}}
--    Unique YouTube recordings linked in a section.
local p = {}
local p = {}


local function esc(s) return (s:gsub('(%W)', '%%%1')) end
local function esc(s) return (s:gsub('(%W)', '%%%1')) end
local function getContent(page)
local t = mw.title.new(page)
if not t then return nil end
return t:getContent()
end
-- Extract the wikitext of one section: from its heading (any level)
-- to the next heading of the same or higher level.
local function getSection(content, section)
local hStart, hEnd, marks = content:find('\n(=+)%s*' .. esc(section) .. '%s*=*')
if not hStart then return nil end
local depth = #marks
local secEnd = #content + 1
local pos = hEnd
while true do
local nStart, nEnd, nMarks = content:find('\n(=+)', pos)
if not nStart then break end
if #nMarks <= depth then secEnd = nStart break end
pos = nEnd
end
return '\n' .. content:sub(hEnd + 1, secEnd - 1)
end
-- Data rows = row separators minus one header separator per table.
local function countRows(body)
local _, tables = body:gsub('\n{|', '')
local _, seps  = body:gsub('\n|%-', '')
return math.max(seps - tables, 0)
end
local function countSection(page, section)
local content = getContent(page)
if not content then return 0 end
local body = getSection(content, mw.text.trim(section))
if not body then return 0 end
return countRows(body)
end


function p.count(frame)
function p.count(frame)
    local section = mw.text.trim(frame.args[1] or '')
return countSection(frame.args.page or 'BOINC projects', frame.args[1] or '')
    local title = mw.title.new(frame.args.page or 'BOINC projects')
end
    if not title then return 0 end
    local content = title:getContent()
    if not content then return 0 end


    -- Find the heading line, any level: ==== Active BOINC Projects ====
function p.total(frame)
    local hStart, hEnd, marks = content:find('\n(=+)%s*' .. esc(section) .. '%s*=*')
local page = frame.args.page or 'BOINC projects'
    if not hStart then return 0 end
local content = getContent(page)
    local depth = #marks
if not content then return 0 end
local sum = 0
for _, s in ipairs(frame.args) do
local body = getSection(content, mw.text.trim(s))
if body then sum = sum + countRows(body) end
end
return sum
end


    -- Section ends at the next heading of the same or higher level
function p.documented(frame)
    local secEnd = #content + 1
local page = frame.args.page or 'Main Page'
    local pos = hEnd
local section = mw.text.trim(frame.args[1] or 'Local BOINC project wiki pages')
    while true do
local content = getContent(page)
        local nStart, nEnd, nMarks = content:find('\n(=+)', pos)
if not content then return 0 end
        if not nStart then break end
local body = getSection(content, section)
        if #nMarks <= depth then secEnd = nStart break end
if not body then return 0 end
        pos = nEnd
local seen, n = {}, 0
    end
for link in body:gmatch('%[%[([^|%]%[#:]+)') do
link = mw.text.trim(link)
if link ~= '' and not seen[link] then
seen[link] = true
local t = mw.title.new(link)
if t and t.namespace == 0 and t.exists then
n = n + 1
end
end
end
return n
end


    local body = '\n' .. content:sub(hEnd + 1, secEnd - 1)
function p.recordings(frame)
    local _, tables = body:gsub('\n{|', '')
local content = getContent(frame.args.page or 'BOINC project screensavers')
    local _, seps  = body:gsub('\n|%-', '')
if not content then return 0 end
    -- each table has one header-row separator; the rest are data rows
local body = getSection(content, mw.text.trim(frame.args[1] or 'Screensaver Gallery'))
    return math.max(seps - tables, 0)
if not body then return 0 end
local seen, n = {}, 0
local function mark(id)
if id and id ~= '' and not seen[id] then
seen[id] = true
n = n + 1
end
end
for id in body:gmatch('youtube%.com/watch%?v=([%w_-]+)') do mark(id) end
for id in body:gmatch('youtu%.be/([%w_-]+)') do mark(id) end
for id in body:gmatch('img%.youtube%.com/vi/([%w_-]+)') do mark(id) end
return n
end
end


return p
return p