Module:Ref info
Appearance
-- This module counts the number of times that various reference tags and cs1|2 templates appear.
-- {{#invoke:ref_count|ref_count}}
-- {{ref info}}
-- {{ref info|aristotle}}
local p = {}
-- these are the cannonical names. What to do about redirects? Lua patterns?
local cs1_template_list = {'ar[Xx]iv', '[Aa][Vv] media', '[Aa][Vv] media notes', 'bio[Rr]xiv', 'book', 'citeseerx', 'conference',
'[Dd][Vv][Dd] notes', 'encyclopedia', 'episode', 'interview', 'journal', 'magazine', 'mailing list', 'map', 'news',
'newsgroup', 'podcast', 'press release', 'report', 'sign', 'speech', 'serial', 'techreport', 'thesis', 'web'};
local cleanup_template_list = {'[Cc]itation needed', '[Dd]isputed inline', '[Dd]ubious', '[Ff]ailed verification'};
local dead_link_template_list = {'[Dd]ea?d[%- ]?[Ll]inks?', '[Dd]ead ?cite', '[Dd]ead page', '[Dd]ead ?url', '[Dd]ead%-inline',
'404', '[Bb]ad ?link', '[Bb]roken ?link', '[Dd][Ll]', '[Ll]ink ?broken', '[Dd]ead'}
local webarchive_template_list = {'[Aa]rchive url', '[Aa]rchive.org', 'IAWM', '[Ii]awm', '[Uu]rl archive', '[Ww]ay[Bb]ack', '[Ww]aybackdate',
'[Ww]eb ?archive?', '[Ww]ebcitation'}
local authors_param_list = {'authors', 'people', 'host', 'credits'};
local last_param_list = {'last1?', 'author1?', 'surname1?', 'author%-last1?', 'author1?%-last', 'subject1?'};
--[[--------------------------< C O U N T _ P A T T E R N >----------------------------------------------------
this is a general purpose function used to count occurrences of patterns in the unparsed article text
]]
local function count_pattern (text, pattern)
local _;
local count;
_, count = mw.ustring.gsub (text, pattern, '%1');
return count;
end
--[[--------------------------< C O U N T _ C S 1 _ L A S T >--------------------------------------------------
Using the list of cs1 templates, make a count of just those cs1 templates that have |last= or |last1= (or any aliases
of these)
]]
local function count_cs1_last (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cs1_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cs1 template
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'last1?' .. '%s*=%s*[^\}]', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'surname1?' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'author1?%-last1?' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ C S 1 _ A U T H O R >----------------------------------------------
Using the list of cs1 templates, make a count of just those cs1 templates that have |author= or |author1= (or any aliases
of these)
]]
local function count_cs1_author (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cs1_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cs1 template
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'author1?' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'subject1?' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ C S 1 _ A U T H O R S >--------------------------------------------
Using the list of cs1 templates, make a count of just those cs1 templates that have |authors= (or any aliases)
]]
local function count_cs1_authors (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cs1_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cs1 template
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'authors' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'people' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'host' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'credits' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ C S 1 _ V A U T H O R S >------------------------------------------
Using the list of cs1 templates, make a count of just those cs1 templates that have |vauthors=
]]
local function count_cs1_vauthors (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cs1_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cs1 template
_, count = mw.ustring.gsub (text, pattern .. '[^}]*|%s*' .. 'vauthors' .. '%s*=%s*', '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ C S 1 _ D A T E S _ D M Y >----------------------------------------
Using the list of cs1 templates, make a count of just those cs1 templates that have |date=DD Month YYYY where
DD is one or two digits or a range DD-DD Month YYYY
]]
local function count_cs1_dates_dmy (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cs1_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cs1 template
_, count = mw.ustring.gsub (text, pattern .. '[^}]+|%s*date%s*=%s*' .. '%d?%d%s+%a+%s+%d%d%d%d', '%1'); -- dd Month yyyy
total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]+|%s*date%s*=%s*' .. '%d?%d[%-–]%d?%d%s+%a+%s+%d%d%d%d', '%1'); -- dd-dd Month yyyy
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ C S 1 _ D A T E S _ M D Y >----------------------------------------
Using the list of cs1 templates, make a count of just those cs1 templates that have |date=Month DD, YYYY where
DD is one or two digits
]]
local function count_cs1_dates_mdy (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cs1_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cs1 template
-- pattern = pattern .. '[^}]+|%s*date%s*=%s*' .. '%a+%s+%d?%d%s,%s+%a+%s%d%d%d%d';
-- _, count = mw.ustring.gsub (text, pattern, '%1'); -- count occurences of that pattern
-- total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]+|%s*date%s*=%s*' .. '%a+%s+%d?%d%s,%s+%a+%s%d%d%d%d', '%1'); -- Month dd, yyyy
total = total + count; -- accumulate a total
_, count = mw.ustring.gsub (text, pattern .. '[^}]+|%s*date%s*=%s*' .. '%a+%s+%d?%d[%-–]%d?%d%s,%s+%a+%s%d%d%d%d', '%1'); -- Month dd-dd, yyyy
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ C S 1 _ D A T E S _ Y M D >----------------------------------------
Using the list of cs1 templates, make a count of just those cs1 templates that have |date=YYYY-MM-DD
]]
local function count_cs1_dates_ymd (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cs1_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cs1 template
pattern = pattern .. '[^}]+|%s*date%s*=%s*' .. '%d%d%d%d%-%d%d%-%d%d';
_, count = mw.ustring.gsub (text, pattern, '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ C S 1 >------------------------------------------------------------
Using the list of cs1 templates, make a count of just those references or templates as dictated by base_pattern.
]]
local function count_cs1 (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cs1_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cs1 template
_, count = mw.ustring.gsub (text, pattern, '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ C L E A N U P >----------------------------------------------------
Using the list of cleanup templates, make a count of those templates as dictated by base_pattern.
]]
local function count_cleanup (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (cleanup_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cleanup template
_, count = mw.ustring.gsub (text, pattern, '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ D E A D _ L I N K S >----------------------------------------------
Using the list of dead link templates, make a count of those templates as dictated by base_pattern.
]]
local function count_dead_links (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (dead_link_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected cleanup template
_, count = mw.ustring.gsub (text, pattern, '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< C O U N T _ W E B A R C H I V E >----------------------------------------------
Using the list of webarchive aliases, make a count of those templates as dictated by base_pattern.
]]
local function count_webarchive (text, base_pattern)
local _;
local count, total = 0, 0;
for i, template in ipairs (webarchive_template_list) do
pattern = string.format (base_pattern, template); -- make a pattern for the selected webarchive template
_, count = mw.ustring.gsub (text, pattern, '%1'); -- count occurences of that pattern
total = total + count; -- accumulate a total
end
return total;
end
--[[--------------------------< H A S _ L D R >----------------------------------------------------------------
returns a string set to 'yes' if the article uses list defined references. ldr uses {{reflist |refs=...}} or
<references>...</references>. Here we do simple 'find's to make the determination.
It is also possible to do ldr with {{refbegin}} ... {{refend}}
the pattern value is passed to this function but ignored
]]
local function has_ldr (text, pattern)
result = {};
if mw.ustring.find (text, '{{%s*[Rr]eflist[^}]*|%s*refs%s*=%s*[^}|]+') then -- does page use {{Reflist |refs=...}}?
return 'yes'
elseif mw.ustring.find (text, '<references>[^<]+') then -- does page use <references>...</references>?
return 'yes'
else
return 'no';
end
end
--[[--------------------------< O B J E C T S T A B L E >----------------------------------------------------
Here we define various properties and values necessary to the counting of referencing objects
]]
local objects = {
['unnamed_refs'] = { -- count unnamed ref tags
['func'] = count_pattern, -- the function that does the work for this object
['pattern'] = '(<ref>)', -- a pattern that the function uses to find and count this object
['count'] = 0, -- the returned result (called count because that is the most common but can be 'yes' or 'no' etc
['label'] = 'unnamed refs' -- a label and marckup for displaying the result; used with string.format()
},
['named_refs'] = { -- count named ref tags
['func'] = count_pattern,
['pattern'] = '(<ref%s+name%s*=%s*[%a%d%p ]+>)',
['count'] = 0,
['label'] = 'named refs'
},
['self_closed_refs'] = { -- count self closed ref tags
['func'] = count_pattern,
['pattern'] = '(<ref%s*name%s*=%s*["%a%d%p ]+/>)',
['count'] = 0,
['label'] = 'self closed'
},
['r_templates'] = { -- count R templates (wrapper for self closed refs)
['func'] = count_pattern,
['pattern'] = '({{%s*[Rr]%s*|)',
['count'] = 0,
['label'] = 'R templates'
},
['refn_templates'] = { -- count R templates (wrapper for self closed refs)
['func'] = count_pattern,
['pattern'] = '({{%s*[Rr]efn%s*|)',
['count'] = 0,
['label'] = 'Refn templates'
},
['bare_url_refs'] = { -- count bare url refs
['func'] = count_pattern, -- TODO: separate function to detect protocol relative urls?
['pattern'] = '(<ref[^>]*>%s*http[^<%s]+%s*</ref>)',
['count'] = 0,
['label'] = '<span style="font-size:inherit" class="error">bare url refs</span>'
},
['ext_link_refs'] = { -- count unlabeled external link refs
['func'] = count_pattern, -- TODO: separate function to detect protocol relative urls?
['pattern'] = '(<ref[^>]*>%[%s*http[^%]<%s]+%][^<]*</ref>)',
['count'] = 0,
['label'] = '<span style="font-size:inherit" class="error">bare ext link refs</span>'
},
['cs1_like_refs'] = { -- count cs1 refs and refs that look like cs1 (cite something)
['func'] = count_pattern,
['pattern'] = '(<ref[^>]*>[^<{]*{{%s*[Cc]ite%s+[^|]+)',
['count'] = 0,
['label'] = 'cs1-like refs'
},
['cs1_refs'] = { -- count cs1 refs only
['func'] = count_cs1,
['pattern'] = '(<ref[^>]*>[^<{]*{{%%s*[Cc]ite%%s+%s%%s*|)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 refs'
},
['cs1_like_templates'] = { -- count cs1 templates and templates that look like cs1
['func'] = count_pattern,
['pattern'] = '({{%s*[Cc]ite%s+[^|]+)',
['count'] = 0,
['label'] = 'cs1-like templates'
},
['cs1_templates'] = { -- count cs1 templates only
['func'] = count_cs1,
['pattern'] = '({{%%s*[Cc]ite%%s+%s%%s*|)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 templates'
},
['cs2_refs'] = { -- count cs2 refs
['func'] = count_pattern,
['pattern'] = '(<ref[^>]*>[^<{]*{{%s*[Cc]itation%s*|)',
['count'] = 0,
['label'] = 'cs2 refs'
},
['cs2_templates'] = { -- count cs2 templates
['func'] = count_pattern,
['pattern'] = '({{%s*[Cc]itation%s*|)',
['count'] = 0,
['label'] = 'cs2 templates'
},
['vcite_refs'] = { -- count vancite, vcite, and vcite2 refs
['func'] = count_pattern,
['pattern'] = '(<ref[^>]*>[^<{]*{{%s*[Vv]a?n?cite2?%s+[^|]+)',
['count'] = 0,
['label'] = 'vcite refs'
},
['vcite_templates'] = { -- count vancite, vcite, and vcite2 templates
['func'] = count_pattern,
['pattern'] = '({{%s*[Vv]a?n?cite2?%s+[^|]+)',
['count'] = 0,
['label'] = 'vcite templates'
},
['wikicite_templates'] = { -- count wikicite templates
['func'] = count_pattern,
['pattern'] = '({{%s*[Ww]ikicite%s*|)',
['count'] = 0,
['label'] = 'wikicite templates'
},
['harv_refs'] = { -- count harv refs
['func'] = count_pattern,
['pattern'] = '(<ref[^>]*>[^<{]*{{%s*[Hh]arv[nbcolptx]*%s*|)',
['count'] = 0,
['label'] = 'harv refs'
},
['harv_templates'] = { -- count harv templates
['func'] = count_pattern,
['pattern'] = '({{%s*[Hh]arv[nbcolptx]*%s*|)',
['count'] = 0,
['label'] = 'harv templates'
},
['sfn_templates'] = { -- count sfn templates
['func'] = count_pattern,
['pattern'] = '({{%s*[Ss]fn[mp]?%s*|)',
['count'] = 0,
['label'] = 'sfn templates'
},
['rp_templates'] = { -- count rp templates
['func'] = count_pattern,
['pattern'] = '({{%s*[Rr]p%s*|)',
['count'] = 0,
['label'] = 'rp templates'
},
['ldr'] = { -- does this article use list defined references?
['func'] = has_ldr,
['pattern'] = '', -- uses multiple patterns which are defined in the function
['count'] = 'no',
['label'] = 'uses ldr'
},
['refbegin_templates'] = { -- count refbegin templates - bibliography lists
['func'] = count_pattern,
['pattern'] = '({{%s*[Rr]efbegin)',
['count'] = 0,
['label'] = 'refbegin templates'
},
['cleanup_templates'] = { -- count cleanup templates
['func'] = count_cleanup,
['pattern'] = '({{%%s*%s)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cleanup templates'
},
['dead_link_templates'] = { -- count deadlink templates (includes redirects)
['func'] = count_dead_links,
['pattern'] = '({{%%s*%s%%s*|)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'dead link templates'
},
['webarchive_templates'] = { -- count webarchive templates (includes redirects)
['func'] = count_webarchive,
['pattern'] = '({{%%s*%s%%s*|)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'webarchive templates'
},
['cs1_dmy_dates'] = { -- count cs1 templates only
['func'] = count_cs1_dates_dmy,
['pattern'] = '({{%%s*[Cc]ite%%s+%s%%s*)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 dmy dates'
},
['cs1_mdy_dates'] = { -- count cs1 templates only
['func'] = count_cs1_dates_mdy,
['pattern'] = '({{%%s*[Cc]ite%%s+%s%%s*)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 mdy dates'
},
['cs1_ymd_dates'] = { -- count cs1 templates only
['func'] = count_cs1_dates_ymd,
['pattern'] = '({{%%s*[Cc]ite%%s+%s%%s*)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 ymd dates'
},
['cs1_last'] = { -- count cs1 templates only
['func'] = count_cs1_last,
['pattern'] = '({{%%s*[Cc]ite%%s+%s%%s*)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 last/first'
},
['cs1_author'] = { -- count cs1 templates only
['func'] = count_cs1_author,
['pattern'] = '({{%%s*[Cc]ite%%s+%s%%s*)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 author'
},
['cs1_authors'] = { -- count cs1 templates only
['func'] = count_cs1_authors,
['pattern'] = '({{%%s*[Cc]ite%%s+%s%%s*)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 authors'
},
['cs1_vauthors'] = { -- count cs1 templates only
['func'] = count_cs1_vauthors,
['pattern'] = '({{%%s*[Cc]ite%%s+%s%%s*)', -- will be modified in the func by string.format()
['count'] = 0,
['label'] = 'cs1 vauthors'
},
}
-- here we set the order in which the objects are processed
local order = {'unnamed_refs', 'named_refs', 'self_closed_refs', -- these three are always output
'r_templates', -- this and the others only produce output when ...
'refn_templates', -- ... their count is not 0 or not 'no'
'bare_url_refs',
'ext_link_refs',
'cs1_refs',
'cs1_templates',
'cs1_like_refs',
'cs1_like_templates',
'cs2_refs', 'cs2_templates',
'vcite_refs', 'vcite_templates',
'wikicite_templates',
'harv_refs', 'harv_templates',
'sfn_templates',
'rp_templates',
'ldr',
'refbegin_templates',
'cleanup_templates',
'dead_link_templates',
'webarchive_templates',
'cs1_dmy_dates',
'cs1_mdy_dates',
'cs1_ymd_dates',
'cs1_last',
'cs1_author',
'cs1_authors',
'cs1_vauthors',
};
--[[--------------------------< P . R E F _ I N F O >----------------------------------------------------------
the working part of Template:Ref info
]]
function p.ref_info(frame)
local text; -- unparsed page content
local title; -- page title without namespace or interwiki references
local nstitle; -- page title with namespace and interwiki references
local page_title_object; --
local output = {};
local i=1;
local style = frame.args.style or ''; -- styling css for output table
if frame.args[1] then
page_title_object = mw.title.new(frame.args[1]); -- title object for the page specified in the template call
else
page_title_object = mw.title.getCurrentTitle(); -- title object for the current page
end
text = page_title_object:getContent(); -- the unparsed content of the selected page
nstitle = page_title_object.prefixedText; -- the title of the page (with namespace)
title = page_title_object.text; -- the title of the page (without namespace)
if nil == text then
return string.format ('<span style="font-size:100%%" class="error">{{ref info}} – page is empty or does not exist: %s</span>', frame.args[1] or 'no page');
end
while order[i] do -- loop through order and search for the related objects
object = order[i]; -- the selected object
objects[object].count = objects[object].func (text, objects[object].pattern) -- do the search and store the result
i=i+1; -- bump to the next object
end
-- for those that count duplicates remove the duplicates from the counts
objects['named_refs'].count = objects['named_refs'].count - objects['self_closed_refs'].count;
objects['cs1_like_refs'].count = objects['cs1_like_refs'].count - objects['cs1_refs'].count;
objects['cs1_like_templates'].count = objects['cs1_like_templates'].count - objects['cs1_templates'].count;
table.insert (output, string.format ('{| class="wikitable" style="text-align:right; %s"\n|+reference info for [[%s|%s]]', style, nstitle, title)); -- output table header
i=1; -- reset the indexer
while order[i] do -- loop through order and output from the related objects
object = order[i]; -- the selected object
if i<=3 then -- first three (reference tags) are always output
table.insert (output, string.format ('%s\n|%s', objects[object].label, objects[object].count));
elseif 'string' == type (objects[object].count) then -- objects[object].count can be a string or a number
if 'no' ~= objects[object].count then -- if a string and not 'no' ...
table.insert (output, string.format ('%s\n|%s', objects[object].label, objects[object].count)); -- output the result
end
elseif 'number' == type (objects[object].count) then -- if a number ...
if 0 < objects[object].count then -- ... and count is greater than zero ...
table.insert (output, string.format ('%s\n|%s', objects[object].label, objects[object].count)); -- ... output the result
end
end
i=i+1; -- bump the indexer
end
local result = table.concat (output,'\n|-\n! scope="row" | ');
return result .. '\n|-\n|scope="row" colspan="2" style="text-align:center"|[[Template:Ref_info#Output_meanings|explanations]]\n|-\n|}'
end
return p