Module:USN fleet totals/data

require('strict');

local patterns_tags = {

'.-',

'',

'

.-
',

'.-',

'.-', -- deprecated alias of syntaxhighlight tag

}

local Article_content;

local grand_total = 0;

local planned_total = 0;

local wikitables_t = {

['Commissioned_t'] = {}, -- k/v table where k is ship type and v is the number of that type

['Non-commissioned_t'] = {},

['Support_t'] = {},

['Ready Reserve Force ships_t'] = {},

['Reserve fleet_t'] = {},

['Under construction_t'] = {},

['On order_t'] = {},

['Retirements_t'] = {}, -- this one is filled separately from the others;

}

local wikitable_names_t = {'Commissioned_t', 'Non-commissioned_t', 'Support_t', 'Ready Reserve Force ships_t', 'Reserve fleet_t', 'Under construction_t', 'On order_t', 'Retirements_t'};

local fleet_wikitables_t = {'Commissioned_t', 'Non-commissioned_t', 'Support_t', 'Ready Reserve Force ships_t', 'Reserve fleet_t'}; -- only these for n_of_m

local headers_t = { -- headers for the fleet totals section

'Commissioned (USS)',

'Non-commissioned (USNS)',

'Support (MV, RV – \'\'or no prefix\'\')',

'Ready Reserve Force ships (MV, SS, GTS)',

'Reserve Fleet ships (USS, USNS)',

'Under construction',

'On order',

'Expected to retire',

}

local totals_t = { -- table to hold total number of ships in these categories

['Commissioned_t'] = 0,

['Non-commissioned_t'] = 0,

['Support_t'] = 0,

['Ready Reserve Force ships_t'] = 0,

['Reserve fleet_t'] = 0,

['Under construction_t'] = 0,

['On order_t'] = 0,

['Retirements_t'] = 0,

}

local n_of_m_t = {} -- table of total counts (m in 'n of m')

local retirements_year_max = 0;

--[[--------------------------< A R T I C L E _ C O N T E N T _ G E T >----------------------------------------

get article content, remove certain html-like tags and their content so that this code doesn't include any citation

templates inside the tags as valid tagets; they are not.

]]

local function article_content_get ()

if not Article_content then

Article_content = mw.title.new ('List of current ships of the United States Navy'):getContent();

for _, tag in ipairs (patterns_tags) do

Article_content = Article_content:gsub (tag, ''); -- remove certain html-like tags and their content

end

while Article_content:match ('([\r\n]+|%-) *[\r\n]+|%-') do -- are there multiple row markers without intervening column data?

Article_content = Article_content:gsub ('([\r\n]+|%-) *[\r\n]+|%-', '%1'); -- remove duplicate row markers

end

end

end

--[[--------------------------< C O L U M N _ I N D E X _ G E T >----------------------------------------------

get the labels from the tops of the current wikitable; we need indexes for '!Type' and '!Note' and return the

associated column number. This function assumes that label markup for each table is one-line-per-label; label

markup all-on-one-line (!

is a

...
delimited wikitable

]]

local function column_index_get (wikitable)

local i = 0;

local type_col, note_col;

for label in wikitable:gmatch ('[\r\n](![^\r\n]+)') do -- spin through each header row

i = i + 1; -- bump the indexer

if label:match ('! *Type') then -- if this header row is for 'Type'

type_col = i; -- save the indexer as column number

elseif label:match ('! *Note') then -- if this header row is for 'Note'

note_col = i; -- save the indexer as column number

end

end

return type_col, note_col;

end

--[[--------------------------< S H I P _ T Y P E S _ G E T >--------------------------------------------------

find first row markup (|-) and fetch the column numbers for Type and Note. Then find each row markup, count

lines until Type column data are located, extract the contents (ship type) and add entry to the appropriate wikitable

in wikitables_t; ship type already present, bump the count.

Continue to locate the Note column. If Note column

holds a {{decommission}} (commissioned ships) or {{end of service}} (non=commissioned ships) template, add ship

type to ; ship type already present, bump the count.

]]

local function ship_types_get (wikitable, wikitable_index)

local find_pattern = '|%-';

local type_label_index, note_label_index;

local tstart, tend = wikitable:find (find_pattern); -- find the table row marker in ; should be column headers

local i=0;

if tstart then -- if we found row marker (|-) for headers

type_label_index, note_label_index = column_index_get (wikitable); -- try to find index of type label (!Type) and note label (!Note)

tstart, tend = wikitable:find (find_pattern, tend); -- look for the next row

else

return nil; -- TODO: error message?

end

while tstart do

local i = 0;

local column_text; -- the text that is the wikitable column markup + content (a row of text that is from: )

local rstart = tend; -- row start and end return values from string.find()

local rend;

local pattern = '[\r\n]+|([^\r\n]*)'; -- newline, pipe, everything before the next newline

local ship_type; -- the ship type; we need this for the Note column

while rstart do -- nil if not found

rstart, rend, column_text = wikitable:find (pattern, rstart); -- get the row's individual column contents

if rstart then

i = i + 1; -- bump the column counter

if type_label_index == i then -- is this the column that has the ship type entry (Type)?

ship_type = column_text; -- save a copy of the Type column text for use with Note for the expected-to-be-retired section of this function

if wikitables_t[wikitable_names_t[wikitable_index]][ship_type] then -- if we found a ship type that we already know about:

wikitables_t[wikitable_names_t[wikitable_index]][ship_type] = wikitables_t[wikitable_names_t[wikitable_index]][ship_type] + 1; -- bump the count

else

wikitables_t[wikitable_names_t[wikitable_index]][ship_type] = 1; -- add ship type to the table with a count of 1 else

end

totals_t[wikitable_names_t[wikitable_index]] = totals_t[wikitable_names_t[wikitable_index]] + 1; -- tally

elseif note_label_index == i then -- here we are looking for {{decommission}} and {{end of service}} templates

if column_text:match ('{{%s*[Dd]ecommission%s*|%s*scheduled') or column_text:match ('{{%s*[Dd]ecommission%s*|%s*proposed') or

column_text:match ('{{%s*[Ee]nd of service%s*|%s*scheduled') or column_text:match ('{{%s*[Ee]nd of service%s*|%s*proposed') then

local year = column_text:match ('{{%s*[Dd]ecommission%s*|%s*scheduled%s*|%s*(%d%d%d%d)') or column_text:match ('{{%s*[Dd]ecommission%s*|%s*proposed%s*|%s*(%d%d%d%d)') or

column_text:match ('{{%s*[Ee]nd of service%s*|%s*scheduled%s*|%s*(%d%d%d%d)') or column_text:match ('{{%s*[Ee]nd of service%s*|%s*proposed%s*|%s*(%d%d%d%d)');

local decom_ship_type;

if year then

decom_ship_type = table.concat ({ship_type, ' (', year, ')'});

if tonumber (year) > retirements_year_max then

retirements_year_max = tonumber (year);

end

else

decom_ship_type = ship_type;

end

if wikitables_t['Retirements_t'][decom_ship_type] then -- if we found a ship type that we already know about:

wikitables_t['Retirements_t'][decom_ship_type] = wikitables_t['Retirements_t'][decom_ship_type] + 1; -- bump the count

else

wikitables_t['Retirements_t'][decom_ship_type] = 1; -- add ship type to the table with a count of 1 else

end

totals_t['Retirements_t'] = totals_t['Retirements_t'] + 1; -- tally

end

end

rstart = rend; -- not yet on the correct column; reset the starting index to the end of the last find()

end

end

tstart, tend = wikitable:find (find_pattern, tend); -- search for another row marker (|-) in ; begin at end of last row marker search

end

end

--[[--------------------------< R E N D E R _ O U T P U T >----------------------------------------------------

extract data from various tables and append to

]]

local function render_output (index, output_string)

local out_t = {}; -- here we compose this section of

local temp_t = {}; -- a sequence of ship types and counts taken from wikitables_t; separate table for sorting

local function sort (a, b)

a = a:gsub ('%[%[(.+)%]%]', '%1'); -- remove outer wikilink markup

b = b:gsub ('%[%[(.+)%]%]', '%1');

a = a:gsub ('%* ', ''); -- remove unordered list markup

b = b:gsub ('%* ', '');

a = a:gsub ('^[^|]+|', ''); -- remove all but the display text from complex wikilinks

b = b:gsub ('^[^|]+|', '');

a = a:gsub (' *– *%d+', ''); -- remove the count

b = b:gsub (' *– *%d+', '');

return a < b;

end

table.insert (out_t, table.concat ({'', headers_t[index], ' – ', totals_t[wikitable_names_t[index]]})); -- header

table.insert (out_t, '{{Div col|colwidth=22em}}'); -- start of columnar data

for ship_type, count in pairs (wikitables_t[wikitable_names_t[index]]) do -- make an unordered, unsorted, list

local ship_type_total = n_of_m_t[ship_type] and table.concat ({' (of ', n_of_m_t[ship_type], ')'}) or '';

if wikitable_names_t[index] == fleet_wikitables_t[index] then

table.insert (temp_t, table.concat ({'* ', ship_type, ' – ', count, ship_type_total})); -- make and add an unordered list item

else

table.insert (temp_t, table.concat ({'* ', ship_type, ' – ', count})); -- make and add an unordered list item

end

end

table.sort (temp_t, sort); -- ascending sort; TODO: is there a better way to get a sorted list of ship types?

table.insert (out_t, table.concat (temp_t, '\n')); -- make a big string and add it to out_t

table.insert (out_t, '{{div col end}}'); -- end of columnar data

table.insert (out_t, '


\n'); -- horizontal rule

output_string = table.concat ({output_string, table.concat (out_t, '\n')}); -- make a big string and add it to output_string

return output_string; -- and done

end

--[[--------------------------< R E N D E R _ T O T A L S >----------------------------------------------------

render a wikitable list of totals; exclude Under construction, on-order, and expected-to-be-retired ships

This is a presentation table so that the numbers are right justified, each above and below the preceding number

]]

local function render_totals (output_string)

local out_t = {'Totals'};

local tally = 0;

table.insert (out_t, '

role="presentation" style="margin-left:1.5em"'); -- start a wikitable indented 1.5em

for i, wikitable_name in ipairs (wikitable_names_t) do -- spin through the list of table names

if 6 > i then -- not under construction of on order

local wikitable = wikitable_name:gsub ('_t', ''); -- remove suffix

table.insert (out_t, '

'); -- add row markup

table.insert (out_t, table.concat ({'| ', wikitable, ':

style="text-align:right" | ', totals_t[wikitable_names_t[i]]})); -- make a row

tally = tally + totals_t[wikitable_names_t[i]]; -- and bump the grand total

else

break;

end

end

grand_total = tally; -- this will be rounded to the nearest integer evenly divisible by 5

table.insert (out_t, '

'); -- add row markup

table.insert (out_t, table.concat ({'| Grand total:

style="text-align:right" | ', tally, ''})); -- add the grand total row

table.insert (out_t, '

'); -- close the wikitable

output_string = table.concat ({output_string, table.concat (out_t, '\n')}); -- make a big string and add it to output_string

return output_string; -- and done

end

--[[--------------------------< N _ O F _ M _ G E T >----------------------------------------------------------

For ship types in commissioned, non-commissioned, support, ready researve, and reserve tables, count the number

of same-type ships so that we can render '(of M)' annotation for those ship types.

First gather a list of ship types that are listed in more than one table. Compare commissioned to non-commissioned;

commissioned to support; etc. Then non-commissioned to support; non-commissioned to ready researve; etc. Continue

until we compare ready researve to reserve. Do not compare reserve to itself.

Second, scroll through the list of duplicates and accumulate tallies of each ship type.

]]

local function n_of_m_get ()

for i, wikitable_name in ipairs (fleet_wikitables_t) do -- for each wikitable

if #fleet_wikitables_t == i then -- when we get to the last wikitable, don't compare it to itself

break;

end

for ship_type, _ in pairs (wikitables_t[wikitable_name]) do

for j=i+1, #fleet_wikitables_t do -- index 1 looks in indexes 2, 3, ...

if wikitables_t[fleet_wikitables_t[j]][ship_type] then

n_of_m_t[ship_type] = 0; -- this ship type appears in more than one wikitable

end

end

end

end

for ship_type, _ in pairs (n_of_m_t) do -- for each ship type in n_of_m table

for _, wikitable_name in ipairs (fleet_wikitables_t) do -- for each wikitable

if wikitables_t[wikitable_name][ship_type] then -- if the ship type is found in this wikitable

n_of_m_t[ship_type] = n_of_m_t[ship_type] + wikitables_t[wikitable_name][ship_type]; -- add to the tally

end

end

end

end

--[[--------------------------< U S N _ S H I P _ C O U N T E R >----------------------------------------------

tables in 'List of current ships of the United States Navy' are (in this order):

Commissioned

Non-commissioned

Support

Ready Reserve Force ships

Reserve fleet

Under construction

On order

count the ship types in the Type columns of these tables, and then render pretty sorted lists of ship types with

their counts.

]]

local function fleet_totals ()

article_content_get (); -- attempt to get this article's content

local wikitable_index = 0;

local find_pattern = '

';

local tstart, tend = Article_content:find (find_pattern); -- find the first wikitable

while tstart do

local wikitable = Article_content:match ('%b{}', tstart); -- get the whole wikitable

if not wikitable then

break; -- wikitable is nil for some reason (missing closing

for example) so declare ourselves done

end

wikitable_index = wikitable_index + 1;

ship_types_get (wikitable, wikitable_index);

tstart, tend = Article_content:find (find_pattern, tend); -- search for another template; begin at end of last wikitable search

end

n_of_m_get ();

local output_string = '';

for i, _ in ipairs (wikitable_names_t) do

output_string = render_output (i, output_string); -- make a pretty unordered list of ship types and counts for each wikitable

end

output_string = render_totals (output_string); -- make a prety unordered list of 'classification' total numbers

local frame = mw.getCurrentFrame(); -- do this to get access to frame:preprocess()

return frame:preprocess (output_string);

end

--[[--------------------------< E X P O R T S >----------------------------------------------------------------

]]

return {

fleet_totals_str = fleet_totals(),

grand_total = grand_total,

planned_total = totals_t['Under construction_t'] + totals_t['On order_t'],

retirements_total = totals_t['Retirements_t'],

retirements_year_max = retirements_year_max, -- for {{USN fleet totals|retire-year}}

decommission_t = { -- for {{decommission}} (commissioned ships)

['scheduled'] = 'Scheduled to be decommissioned',

['proposed'] = 'Proposed to be decommissioned',

},

end_of_service_t = { -- for {{end of service}} (non-commissioned ships)

['scheduled'] = 'Scheduled end of service',

['proposed'] = 'Proposed end of service',

},

}