--[[--------------------------< C S 1 _ C H A R T S >----------------------------------------------------------
Calls Module:Chart to display various related category counts. Data (the categories) are listed directly by name
(without name space) in Module:CS1 charts/data or (for cs1|2 categories) may be extracted from Module:Citation/CS1/Configuration
Bar charts are rendered left to right in the order listed; pie charts are descending-sorted by count and then
ascending-sorted by category name. The number of 'bars' displayed in a bar chart is limited to 64; the number
of 'slices; displayed in a pie chart is limited to 26.
]]
require('Module:No globals');
local getArgs = require ('Module:Arguments').getArgs;
local data = mw.loadData ('Moduli:CS1_charts/data');
local page_title = mw.title.getCurrentTitle().prefixedText; -- namespace and name
--[[--------------------------< S U B C A T _ D A T A _ F E T C H >--------------------------------------------
return the total number of pages, files, and subcats in a named category's (<cat>) subcategories; when no subcats
listed in <subcats_t>, returns 0
]]
local function subcat_data_fetch (cat)
local page_count = 0;
if data.subcats_t[cat] then -- if this category has listed subcats
local pages_in_cat_t = {};
for i, subcat in ipairs (data.subcats_t[cat]) do -- for each subcat
pages_in_cat_t = mw.site.stats.pagesInCategory (subcat, '*'); -- get the various counts and
page_count = page_count + pages_in_cat_t.files + pages_in_cat_t.pages + pages_in_cat_t.subcats; -- tally
end
end
return page_count;
end
--[[--------------------------< B A R _ C H A R T _ C R E A T E >----------------------------------------------
creates a bar chart of up to 64 categories; empty categories are not included in the chart. Display order (left-
to-right) of the categories in the rendered chart is established in the data sequence tables in ~/data (first-
to-last).
{{#invoke:CS1 charts|bar_chart_create|maint}} -- cs1|2 maintenance categories
{{#invoke:CS1 charts|bar_chart_create|error}} -- cs1|2 error categories
]]
local function bar_chart_create (frame)
local page_counts_t = {};
local subcat_counts_t = {};
local cat_names_t = {};
local bar_chart_x_legends;
local cats_t = data.keyword_cat_map_t[frame.args[1]];
local cats_t_count;
local x_legend = data.keyword_x_legends_map_t[frame.args[1]];
bar_chart_x_legends = (page_title == ('Kategoria:' .. x_legend)) and x_legend or table.concat ({'[[:Kategoria:', x_legend, '|', x_legend, ']]'});
for i, cat in ipairs (cats_t) do
local pages_in_cat_t = {};
cat_names_t[i] = cat; -- save a copy of the category name
pages_in_cat_t = mw.site.stats.pagesInCategory (cat, '*'); -- get the table of counts
if 0 ~= subcat_data_fetch (cat) then
page_counts_t[i] = pages_in_cat_t.files + pages_in_cat_t.pages + subcat_data_fetch (cat); -- don't include pages_in_cat_t.subcats in tally
subcat_counts_t[i] = pages_in_cat_t.subcats; -- but remember for later annotation
else
page_counts_t[i] = pages_in_cat_t.files + pages_in_cat_t.pages; -- there are no subcats so don't bother
end
cats_t_count = i;
end
local out = {'chart', 'bar chart'}; -- init for #invoke parser function call with Module:Chart and function barChart()
out.delimiter = data.bar_chart_delimiter; -- |delimiter=
out['units suffix'] = data.bar_chart_units_suffix; -- |units suffix=
local tail;
local group_names_t = {};
local colors_t = {};
local j = 0; -- indexer for cats that that are not empty
for i, v in ipairs (cats_t) do
if 0 ~= page_counts_t[i] then
j = j + 1; -- bump the indexer
out['group ' .. j] = page_counts_t[i]; -- add |group 1= to |group n= pararameters
if subcat_counts_t[i] then -- if this cat has subcats
tail = table.concat ({' faqe nga ', subcat_counts_t[i], ' nënkategori'}) -- modify the group name tail
else -- here when no subcats
tail = ' faqe'; -- standard group name tail
end
out['tooltip ' .. j] = table.concat ({cat_names_t[i], ' ', page_counts_t[i], tail}); -- add |tooltip 1= to |tooltip n= pararameters
out['links ' .. j] = table.concat ({':Kategoria:', cat_names_t[i]}); -- add |links 1= to |links n= pararameters
table.insert (group_names_t, table.concat ({'[[:Kategoria:', cat_names_t[i], '|', cat_names_t[i], ']] ', page_counts_t[i], tail}));
if 64 == j then
break;
end
end
end
out['group names'] = table.concat (group_names_t, data.bar_chart_delimiter); -- add |group names= parameter
for i, color in ipairs (data.chart_colors_t) do -- make a local table for concatenation; necessary because <data.chart_colors_t> is a metatable
colors_t[i] = color;
if i == j then -- no more than we need
break;
end
end
out['colors'] = table.concat (colors_t, data.bar_chart_delimiter, 1, j); -- add |colors= parameter
out['x legends'] = table.concat ({bar_chart_x_legends, ' (', #page_counts_t-j, ' nga ', cats_t_count, ' kategori bosh janë fshehur)'}); -- add |x legends=
return frame:callParserFunction ('#invoke', out); -- {{#invoke:chart|bar chart|args...}}
end
--[[--------------------------< P I E _ C H A R T _ C R E A T E >----------------------------------------------
creates a pie chart of up to 26 slices; empty categories are not included in the chart. Display order of the
categories in the rendered chart is by descending count and then by ascending category name (largest-to-smallest
slice)
{{#invoke:CS1 charts|pie_chart_create|lang}} -- cs1|2 language properites categories
{{#invoke:CS1 charts|pie_chart_create|script}} -- cs1|2 script-language properties categories
]]
local function pie_chart_create (frame)
local args_t = getArgs (frame);
local raw = {}; -- count, legend, and category extracted from category
local slices = {}; -- formatted output suitable for [[Module:Chart]] |slices= parameter
local link = true; -- slices are linked
local delimiter = ';'; -- default is ':' but catagory names have colons so use semicolon
local cats_t = data.keyword_cat_map_t[frame.args[1]];
for _, cat in ipairs (cats_t) do -- spin through category names and construct raw data for chart
local t = {}
table.insert (t, mw.site.stats.pagesInCategory (cat, 'pages'));
table.insert (t, cat and cat:match (args_t.pattern or '.*') or cat); -- extract legend; use cat name if pattern not provided
table.insert (raw, t); -- save this
end
if 0==#raw then
return string.format ('(%s%s%s%s%s)', -1, delimiter, 'Gabim: Grafiku nuk ka asnjë pjesë të përcaktuar', delimiter, '#d33');
end
for i, v in ipairs (raw) do -- look for duplicate names
for j=i+1, #raw do
if raw[i][2] == raw[j][2] then
return string.format ('(%s%s%s %s%s%s)', -1, delimiter, 'Gabim: Grafiku ka disa pjesë me emra të njëjta', raw[i][2], delimiter, '#d33');
end
end
end
local function comp (a, b) -- used in following table.sort()
if a[1] == b[1] then -- when same do
return a[2] < b[2]; -- ascending alpha sort on name
end
return tonumber (a[1]) > tonumber(b[1]); -- descending sort
end
table.sort (raw, comp); -- descending sort
local non_empty_count = 0; -- count of categories with at least one page
local empty_count = 0;
local other_pages_tally = 0; -- tally of pages not included in the first 25 slices
for i, t in ipairs (raw) do
if 26 > i and 0 ~= t[1] then -- slices 1 - 25 separately in the chart (as long as they have something in them)
local slice_link = table.concat ({'[[:Kategoria:', t[2], '|', t[2], ']]'});
table.insert (slices, table.concat ({
'(',
t[1], -- count
delimiter,
slice_link,
delimiter,
data.chart_colors_t[i], -- color for this slice
delimiter,
slice_link,
')'
}));
elseif 0 ~= t[1] then -- would-be slices 26+
non_empty_count = non_empty_count + 1; -- count the number of non-empty cats
if t[1] then -- in case t[1] is nil for whatever reason; shouldn't be
other_pages_tally = other_pages_tally + t[1]; -- sum the number of pages in these non-empty cats
end
else
empty_count = empty_count + 1; -- count the number of empty cats
end
end
if 0 == #slices then -- nothing in slices{}
return string.format ('(%s%s%s%s%s)', -1, delimiter, 'Gabim: Vlera e të gjitha pjesëve të përcaktuara është 0', delimiter, '#d33');
end
if 0 ~= non_empty_count or 0 ~= empty_count then -- 26th slice
table.insert (slices, string.format ('(%s%s%s kategori të tjera + %s kategori bosh)', other_pages_tally, delimiter, non_empty_count, empty_count));
end
local out = {'chart', 'pie chart'}; -- init for #invoke parser function call with Module:Chart and function pieChart()
out['delimiter'] = ';';
out['units suffix'] = '_faqe';
out['percent'] = 'true';
out['slices'] = table.concat (slices, '\n');
local label = data.keyword_label_map_t[frame.args[1]]; -- create label for pie chart
local pie_chart_label = (page_title == ('Kategoria:' .. label)) and label or table.concat ({'[[:Kategoria:', label, '|', label, ']]'}); -- don't self link
local render_t = {}; -- add label above pie chart
table.insert (render_t, '<div style="max-width:300px; text-align:center"><span style="font-size:130%">'); -- center label above pie chart
table.insert (render_t, pie_chart_label); -- the label
table.insert (render_t, '</span><div style="text-align:left">'); -- and more of the centering markup
table.insert (render_t, frame:callParserFunction ('#invoke', out)); -- render the chart
table.insert (render_t, '</div></div>'); -- and the last of the centering markup
return table.concat (render_t); -- make a big string and done
end
--[[--------------------------< C A T _ L I S T S _ C H E C K >------------------------------------------------
Create lists of keys that are in one of Moduli:Citation/CS1/Configuration or Moduli:CS1 charts/data.
Keys for category names that are assembled on-the-fly appear only in ~/data and are never available in ~/Configuration.
For keys available in ~/Configuration but not in ~/data, the list includes the category name. It is not possible
to go the other way because ~/data does not know the category names.
{{#invoke:CS1 charts|cat_list_check}}
]]
local function cat_list_check (frame)
local error_conditions_t = mw.loadData ('Moduli:Citation/CS1/Configuration').error_conditions;
local data_err_cats_t = {};
local data_maint_cats_t = {};
for _, index in ipairs (data.error_cats_order_t) do
data_err_cats_t[index] = true;
end
for _, index in ipairs (data.maint_cats_order_t) do
data_maint_cats_t[index] = true;
end
---------- -- see if we can find data.error_cats_order_t keys in ~/Configuration error_conditions table
local in_data_only_t = {}; -- holds list of error and maint keys not found in ~/Configuration
for index, _ in pairs (data_err_cats_t) do
if not error_conditions_t[index] then -- when data key not found
table.insert (in_data_only_t, '\t' .. index); -- add to our list with leading tab
end
end
-- now see if we can find data.maint_cats_order_t keys in ~/Configuration error_conditions table
for index, _ in pairs (data_maint_cats_t) do
if not error_conditions_t[index] then -- when data key not found
table.insert (in_data_only_t, '\t' .. index); -- add to our list with leading tab
end
end
---------- -- now see if we can find ~/Configuration error_conditions keys in ~/data tables
local in_cfg_only_t = {}; -- holds list of error and maint keys not found in ~/data
for index, v_t in pairs (error_conditions_t) do
if index:find ('err_', 1, true) then -- do 'error' keys first
if not data_err_cats_t[index] then -- when config key not found
table.insert (in_cfg_only_t, table.concat ({ -- add to our list
'\t', -- leading tag character
index, -- the key
string.rep ('\t', 10), -- a string of tab character
'-- ', -- comment token
v_t.category, -- and category name
}));
end
else -- here for maint_ keys
if not data_maint_cats_t[index] then -- when config key not found
table.insert (in_cfg_only_t, table.concat ({ -- add to our list
'\t', -- leading tag character
index, -- the key
string.rep ('\t', 10), -- a string of tab character
'-- ', -- comment token
v_t.category, -- and category name
}));
end
end
end
local out_t = {}; -- final output goes here
table.sort (in_data_only_t); -- ascending sorts
table.sort (in_cfg_only_t);
table.insert (out_t, '<pre>'); -- open; wrap in <pre>...</pre> tags so tab characters are collapsed by browsers
if 0 < #in_cfg_only_t then -- if the table is not empty
table.insert (out_t, 'error and maintenance category keys not found in [[Moduli:CS1 charts/data]]:'); -- add header at top of list
table.insert (out_t, table.concat (in_cfg_only_t, '\n')); -- add the list
table.insert (out_t, '\n'); -- add an extra newline
end
if 0 < #in_data_only_t then -- if the table is not empty
table.insert (out_t, 'error and maintenance category keys not found in [[Moduli:Citation/CS1/Configuration]]:'); -- add header at top of list
table.insert (out_t, table.concat (in_data_only_t, '\n')); -- add the list
end
table.insert (out_t, '</pre>'); -- close
return table.concat (out_t, '\n\n'); -- concatenate into a big string and done
end
--[[--------------------------< E X P O R T S >----------------------------------------------------------------
]]
return {
bar_chart_create = bar_chart_create,
pie_chart_create = pie_chart_create,
cat_list_check = cat_list_check,
}