Module:European and national party data

require ('strict');

local get_args = require ('Module:Arguments').getArgs; -- function to fetch frame and parent frame arguments

local cfg = mw.loadData ('Module:European and national party data/config'); -- defines, configuration data, and i18n support

local namespace = mw.title.getCurrentTitle().namespace; -- used for categorization

--[[==========================< S E C T I O N _ 1 : F U N C T I O N A L _ F U N C T I O N S >=======================

These are generic functions to accomplish specific, non-party-related tasks.

]]

--[[--------------------------< S U B S T I T U T E >----------------------------------------------------------

substitutes $1, $2, etc in with data from . Returns plain-text substituted string when

not nil; returns else.

]]

local function substitute (message, data_t)

return data_t and mw.message.newRawMessage (message, data_t):plain() or message;

end

--[[--------------------------< M A K E _ E R R O R _ M S G >--------------------------------------------------

assembles an error message from template name, message text, help link, and error category.

]]

local function make_error_msg (msg, template_name, nocat)

local category;

local category_link = ((0 == namespace) and not nocat) and substitute ('Category:$1', {cfg.settings_t.err_category}) or '';

return substitute ('Error: {{$1}}: $2 ($3)$4',

{

template_name or cfg.settings_t.template_name or frame:getParent():getTitle(), -- the template name without namespace

msg, -- the error message

cfg.settings_t.help, -- help wikilink display text

category_link -- link to error category (main namespace only)

})

end

--[[--------------------------< R O U N D >--------------------------------------------------------------------

return the rounded value of the arguments with two decimals

]]

local function round (n)

return math.floor(100 * n + 0.5) / 100 -- round argument to two decimals

end

--[[--------------------------< S T R I P _ U R L >--------------------------------------------------------------------

return the stripped down URL with a hyperlink

]]

local function strip_URL (URL)

local new_URL = URL;

local patterns_t = {'^%[%[(.*)%]%]', '^https://www.', '^http://www.', '^https://', '^http://', '^www.', '/$'}; -- valid patterns

for i, pattern in ipairs (patterns_t) do -- loop through the patterns in

new_URL = new_URL.gsub(new_URL, pattern, "");

end

return "[" .. URL .. " " .. new_URL .. "]";

end

--[[--------------------------< T R A N S L A T E >--------------------------------------------------------------------

translate a term based on the project language

]]

local function xlate (term)

if term then

--local new_term = string.gsub(term, " ", "_");

if 'en' == cfg.this_project_language then

return term;

elseif not cfg.xlate_t[cfg.this_project_language] then

return term;

elseif cfg.xlate_t[cfg.this_project_language][term] then

return cfg.xlate_t[cfg.this_project_language][term];

else

return term;

end

end

end

--[[--------------------------< T R A N S L A T E _ P A R A M >--------------------------------------------------------------------

translate a parameter based on the project language

]]

local function xlate_param (parameter)

if 'en' == cfg.this_project_language then

return parameter;

elseif not cfg.xlate_param_t[cfg.this_project_language] then

return parameter;

elseif cfg.xlate_param_t[cfg.this_project_language][parameter] then

return cfg.xlate_param_t[cfg.this_project_language][parameter];

else

return parameter;

end

end

--[[--------------------------< L O C A L _ P A R A M E T E R _ T O _ V A L U E _ O R _ B O O L E A N >--------------------------------------------------------------------

turns a given parameter into its local form and into a boolean

]]

local function local_parameter_to_value_or_boolean (frame, parameter, outcome) -- to test calls and functions, for verification purposes

local args_t = get_args (frame); -- get arguments; empty string or whitespace positional parameters set to nil

local localised_parameter = xlate_param (parameter); -- translate given parameter from English into local language

local argument = (args_t[tostring(localised_parameter)] and args_t[tostring(localised_parameter)]:lower()) or (args_t[parameter] and args_t[parameter]:lower()); -- get argument (in local language) matching parameter in local language

if outcome == 'boolean' then

if nil == argument then -- if no argument, return no

argument = false;

else

argument = xlate (argument); -- translate argument into English

argument = 'yes' == argument; -- turn argument into boolean

end

elseif nil ~= argument and argument:match ('^#') then

argument = argument.gsub(argument, "#", "#");

end

return argument;

end

--[[--------------------------< X L A T E _ W I K I D A T A >--------------------------------------------------------------------

call the {{wd}} or {{wikidata}} template based on language

arguments are as follows:

  • data_requested: the type of data sought -- 'qualifier', 'property', 'references', 'label' -- compulsory
  • qid: the Wikidata item on which data is sought -- e.g. Q208242 for the European People's Party -- not compulsory
  • property_id: the property for which data is sought -- e.g. P1410 for number of seats in assembly -- compulsory
  • qualifier_id: qualifier for property -- e.g. P194 for legislative body -- not compulsory
  • value_of_qualifier: value of the qualifier -- e.g. P8889 for the European Parliament -- only compulsory if qualifier_id is provided
  • reference: whether a reference should be displayed -- e.g. reference=yes to display the reference, anything else (preferably reference=no) otherwise; adding an "s" will display several references
  • preferred: whether the preferred value should be displayed -- e.g. preferred=yes to display the preferred value, anything else (preferably preferred=no) otherwise
  • raw: whether the qID should be displayed -- e.g. raw=yes to display the qID, anything else (preferably raw=no) otherwise
  • linked: whether the value should be linked -- e.g. linked=yes to display the link, anything else (preferably linked=no) otherwise

]]

local function xlate_wikidata (data_requested, qid, property_id, qualifier_id, value_of_qualifier, reference, preferred, raw, linked)

mw.logObject( {data_requested, qid, property_id, qualifier_id, value_of_qualifier, reference, preferred, raw, linked}, "xlate_wikidata()" )

local local_reference = reference;

local local_preferred = preferred;

local local_raw = raw;

local local_linked = linked;

local Wd = require('Module:European_and_national_party_data/Wd')

if local_reference == 'reference=yes' then local_reference = 'reference' elseif local_reference == 'references=yes' then local_reference = 'references' else local_reference = '' end;

if local_preferred == 'preferred=yes' then local_preferred = 'preferred' else local_preferred = '' end;

if local_raw == 'raw=yes' then local_raw = 'raw' else local_raw = '' end;

if local_linked == 'linked=yes' then local_linked = 'linked' else local_linked = '' end;

local wdargs = {local_reference, local_preferred, local_raw, local_linked, property_id}

if qid then

table.insert(wdargs, 5, qid)

end

if '' ~= qualifier_id then

if '' ~= value_of_qualifier then

wdargs[qualifier_id] = value_of_qualifier;

else

table.insert(wdargs, qualifier_id)

end

end

mw.logObject( wdargs, "wdargs" )

return Wd['_' .. data_requested](wdargs);

end

--[[--------------------------< X L A T E _ C O M P O S I T I O N _ B A R >--------------------------------------------------------------------

call the {{composition bar}} template based on language

arguments are as follows:

  • seats: the number of seats to be displayed;
  • total_seats: the total number of seats of the institution in question;
  • color: the color of the bar;
  • width: the width of the bar;
  • percentage: whether or not to display the percentage value;
  • background_color: the background color of the bar;
  • border: the color of the border;

]]

local function xlate_composition_bar (frame, seats, total_seats, color, width, percentage, background_color, border)

if 'fr' == cfg.this_project_language then

return frame:expandTemplate ({title='Infobox Parti politique/Sièges', args = {seats, total_seats, ['hex'] = color}});

elseif 'it' == cfg.this_project_language then

return frame:expandTemplate ({title='Seggi', args = {seats, total_seats, ['c'] = color}});

elseif 'es' == cfg.this_project_language then

return frame:expandTemplate ({title='Composition bar', args = {seats, total_seats, ['hex'] = color}});

elseif 'nl' == cfg.this_project_language then

local comp_bar_args_t = {

seats,

total_seats,

color,

width=width,

per=percentage,

['background-color'] = background_color,

border = border,

};

return frame:expandTemplate ({title='Percentagebalk', args = comp_bar_args_t});

else -- for all other languages the assumption is that the template is called composition bar

local comp_bar_args_t = {

seats,

total_seats,

color,

width=width,

per=percentage,

['background-color'] = background_color,

border = border,

};

return frame:expandTemplate ({title='Composition bar', args = comp_bar_args_t});

end

end

--[[==========================< S E C T I O N _ 2 : V A L I D A T I O N _ F U N C T I O N S >=======================

These are functions to ensure data validation and return error messages when relevant.

]]

--[[--------------------------< V A L I D A T E _ W I D T H >--------------------------------------------------

validates data format for the width parameter of composition bars; returns boolean true when valid; nil else

]]

local function validate_width (width)

local patterns_t = {'^%d+$', '^%d+px$', '^%d+%%$', '^%d+em$'}; -- valid patterns

for i, pattern in ipairs (patterns_t) do -- loop through the patterns in

if width:match (pattern) then -- is there a match?

return true; -- yes, done

end

end

end

--[[--------------------------< H A R M O N I S E _ H O U S E _ T Y P E >----------------------------------------------

harmonises house type input (for calls relating to lower and upper houses)

]]

local function harmonise_lower_upper_house_name (house_type)

if house_type == "LOWER-HOUSE" or house_type == "LOWER" or house_type == "MS-LOWER-HOUSE"then -- the three accepted formats for input

return "LOWER_HOUSE"; -- format actually used in the code

elseif house_type == "UPPER-HOUSE" or house_type == "UPPER" or house_type == "MS-UPPER-HOUSE" then -- the three accepted formats for input

return "UPPER_HOUSE"; -- format actually used in the code

else

return house_type;

end

end

--[[--------------------------< V A L I D A T E _ I N S T I T U T I O N >----------------------------

when institution parameter exists, checkes that it is correct; returns boolean true when valid; error message else

]]

local function validate_institution (institution, template_name)

if institution and not cfg.institutions_t[institution] and not cfg.national_institutions_t[institution] then -- if institution is present, it must be known

return make_error_msg (substitute (cfg.error_messages_t.unknown_inst, {institution}), template_name);

end

return true;

end

--[[==========================< S E C T I O N _ 3 : G E T _ F U N C T I O N S >=======================

These are functions to grab data from Wikidata

]]

--[[--------------------------< G E T _ D A T A >----------------------------------------------------------

general function to return data for the requested property for a party; required to expand {{wikidata}} template

]]

local function allpp_get_data (frame, party, property_id, option)

local template_name = frame:getParent():getTitle();

local args_t = get_args (frame);

local data = "";

local reference = local_parameter_to_value_or_boolean (frame, 'reference', 'boolean'); -- check verbose and make boolean

reference = (reference and option ~= "raw-noref") and 'references=yes' or ''; -- if reference is true, then the call will return a reference

local verbose = local_parameter_to_value_or_boolean (frame, 'verbose', 'boolean'); -- check verbose and make boolean

local raw = (option == "raw" or option == "raw-noref") and "raw=yes" or "raw=no"; -- check raw and format correctly

local linked = (option == "linked") and "linked=yes" or "linked=no"; -- check linked and format correctly

local party_qid = ""; -- init party_qid

if not party then -- party is required

return make_error_msg (substitute (cfg.error_messages_t.unknown_party, {party}), template_name);

elseif cfg.parties_t[party] then -- if party is a European party

party_qid = cfg.parties_t[party];

elseif cfg.alliances_t[party] then -- if party is a European alliance

party_qid = cfg.alliances_t[party];

elseif party:match ('^Q%d+$') then -- if party is a qID

party_qid = party;

elseif property_id == 'P465' and (party == "ALL" or party == "IND" or party == "NONE") then -- default colour for special parameters

return '#BBB';

elseif party ~= "THISPARTY" then -- error message if no party is provided

return make_error_msg (substitute (cfg.error_messages_t.unknown_party, {party}), template_name);

end

if not party then -- direct call when no party is specified

party_qid = "" -- This should not be needed since the function

end -- should error out above if no party specified.

if property_id == "label" then -- if the label of the Wikidata element is requested

data = xlate_wikidata ('label', party_qid, , , '', 'reference=no', 'preferred=no', raw, linked);

elseif property_id == "individual members date" then -- special case

data = xlate_wikidata ('qualifier', party_qid, 'P2124', 'P585', '', 'reference=no', 'preferred=yes', raw, linked);

else -- for all other cases

data = xlate_wikidata ('property', party_qid, property_id, , , reference, 'preferred=no', raw, linked);

end

if property_id == cfg.data_prop_t.colour then -- if no colour, then default colour

if '' == data then

data = '#BBB';

else

data = '#' .. data;

end

elseif property_id == cfg.data_prop_t.website then -- if no website, then empty string

if '' == data then

data = "";

else

data = strip_URL (data);

end

end

if '' == data and verbose then -- if verbose, then error message

return make_error_msg (substitute (cfg.error_messages_t.no_data, {party_qid, property_id}));

end

return data;

end

--[[--------------------------< H O U S E _ Q I D _ F R O M _ M E M B E R _ S T A T E >----------------------------------------------------------

return the qID of a house based on its name (lower house or upper house) and its member state; required to expand {{wikidata}} template

]]

local function house_qid_from_member_state_qid (house_type, member_state_qid)

local house_qid = "";

for row, _ in ipairs (cfg.ms_data_t) do -- loop on table with lower/upper houses per member state

if member_state_qid == cfg.ms_data_t[row].member_state_qid then

if house_type == "LOWER_HOUSE" then

house_qid = cfg.ms_data_t[row].lower_house_qid; -- get the lower house qid

elseif house_type == "UPPER_HOUSE" then

house_qid = cfg.ms_data_t[row].upper_house_qid; -- get the upper house qid

end

end

end

return house_qid;

end

--[[--------------------------< I N S T I T U T I O N _ S I Z E >----------------------------------------------------------

return the size of an institution; required to expand {{wikidata}} template

]]

local function institution_size (frame, institution)

local institution_size = xlate_wikidata ('property', cfg.institutions_t[institution], 'P1342', , , 'reference=no', 'preferred=no', 'raw=no', 'linked=no'); -- get the institution size

if '' == institution_size then -- if no institution size

return make_error_msg (substitute (cfg.error_messages_t.unknown_inst, {institution}));

end

return institution_size;

end

--[[--------------------------< M E M B E R _ S T A T E _ D E L E G A T I O N _ S I Z E >----------------------------------------------------------

return the size of a member state's delegation in the European Parliament; required to expand {{wikidata}} template

]]

local function member_state_delegation_size (frame, member_state_qid)

local delegation_size = xlate_wikidata ('property', member_state_qid, 'P1410', 'P194', 'Q8889', 'reference=no', 'preferred=no', 'raw=no', 'linked=no'); -- get the delegation size

if '' == delegation_size then -- if no delegation size

return make_error_msg (substitute (cfg.error_messages_t.not_member_state, {member_state_qid}));

end

return delegation_size;

end

--[[--------------------------< S I N G L E _ H O U S E _ S E A T S >------------------------------------------------

returns the number of seats in a given lower or upper house (identified by a row in the master table) from

wikidata. When not recognized or when does not have 'that' house, returns 0

]]

local function single_house_seats (frame, row, house_type)

local house_qid;

if house_type == "LOWER_HOUSE" then

house_qid = cfg.ms_data_t[row].lower_house_qid; -- get the lower house qid

elseif house_type == "UPPER_HOUSE" then

house_qid = cfg.ms_data_t[row].upper_house_qid; -- get the upper house qid

end

return house_qid and xlate_wikidata ('property', house_qid, 'P1342', , , 'reference=no', 'preferred=no', 'raw=no', 'linked=no') or 0; -- house_qid is nil when no upper or lower house

end

--[[--------------------------< T O T A L _ H O U S E _ S E A T S >------------------------------------------------

returns the total number of seats of all lower or upper houses in all member states.

]]

local function total_house_seats (frame, house_type)

local sum_seats = 0; -- init sum of seats of European party's member parties in house_type

for row, _ in ipairs (cfg.ms_data_t) do -- for all member states

sum_seats = sum_seats + single_house_seats (frame, row, house_type); -- increase sum_seats

end

return sum_seats;

end

--[[--------------------------< A L L P P _ S E A T S _ R E F >---------------------------------------------------------------

return the reference for a seat claim; required to expand {{wikidata}} template

]]

local function allpp_seats_ref (frame, party, institution)

local args_t = get_args (frame);

local party_type = args_t[1] and args_t[1]:lower(); -- force to lower case

party_type = xlate (party_type);

if institution == "LOWER_HOUSE" or institution == "UPPER_HOUSE" then

if party_type == 'european_entity' then

return '';

elseif party_type == 'national_party' then

if party == "THISPARTY" then

local member_state_qid = allpp_get_data (frame, mw.wikibase.getEntityIdForCurrentPage(), cfg.data_prop_t.country, 'raw-noref');

local house_qid = house_qid_from_member_state_qid (institution, member_state_qid)

return xlate_wikidata ('references', '', 'P1410', 'P194', house_qid, 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

elseif party:match ('^Q%d+$') then

local member_state_qid = allpp_get_data (frame, party, cfg.data_prop_t.country, 'raw-noref');

local house_qid = house_qid_from_member_state_qid (institution, member_state_qid)

return xlate_wikidata ('references', party, 'P1410', 'P194', house_qid, 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

end

end

elseif institution == 'EC' or institution == 'EUCO' then

if party == "THISPARTY" then

return xlate_wikidata ('references', '', 'P1410', 'P208', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

elseif cfg.parties_t[party] then

return xlate_wikidata ('references', cfg.parties_t[party], 'P1410', 'P208', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

elseif cfg.alliances_t[party] then

return xlate_wikidata ('references', cfg.alliances_t[party], 'P1410', 'P208', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

elseif party:match ('^Q%d+$') then

return xlate_wikidata ('references', party, 'P1410', 'P208', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

end

else

if party == "THISPARTY" then

return xlate_wikidata ('references', '', 'P1410', 'P194', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

elseif cfg.parties_t[party] then

return xlate_wikidata ('references', cfg.parties_t[party], 'P1410', 'P194', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

elseif cfg.alliances_t[party] then

return xlate_wikidata ('references', cfg.alliances_t[party], 'P1410', 'P194', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

elseif party:match ('^Q%d+$') then

return xlate_wikidata ('references', party, 'P1410', 'P194', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

end

end

end

local function allpp_get_seats_ref_qid (frame, institution_qid)

return xlate_wikidata ('references', '', 'P1410', 'P194', institution_qid, 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

end

--[[==========================< S E C T I O N _ 4 : S U M _ F U N C T I O N S >=======================

These functions are the ones providing seat numbers for European or national parties, in European institutions or national lower/upper houses

  • functions starting with eupp apply to European parties;
  • functions starting with npp apply to national political parties; and
  • functions starting with allpp apply to all political parties.

]]

--[[--------------------------< A L L P P _ M A K E _ S H A R E _ I N _ E U _ I N S T I T U T I O N S >--------------------------------------------------------------

return the share of a party's seats relative to the total size of a given institution listed in or (so not including lower and upper houses)

required to expand {{wikidata}} template

Note: P1342 is the property "number of seats", used to record an institution's number of seats

]]

local function allpp_make_share_in_eu_institutions (frame, party_seats, institution)

local args_t = get_args (frame);

local party_type = args_t[1] and args_t[1]:lower(); -- force to lower case

local party = args_t[3] and args_t[3]:upper(); -- force to lower case

local constituency = local_parameter_to_value_or_boolean (frame, 'constituency'); -- argument overriding the house's number of seats

party_type = xlate (party_type);

party = xlate (party);

if constituency then

return tonumber (party_seats) and round (100 * party_seats / constituency) or party_seats;

elseif party_type == "national_party" and institution == "EP" then

local member_state_qid = allpp_get_data (frame, party, cfg.data_prop_t.country, 'raw-noref');

return tonumber (party_seats) and round (100 * party_seats / member_state_delegation_size (frame, member_state_qid)) or party_seats;

else

return tonumber (party_seats) and round (100 * party_seats / xlate_wikidata ('property', cfg.institutions_t[institution], 'P1342', , , 'reference=no', 'preferred=no', 'raw=no', 'linked=no')) or party_seats;

end

end

--[[--------------------------< A L L P P _ S E A T S _ I N _ E U _ I N S T I T U T I O N S >------------------------------------------------------------------

return the number of seats occupied by a political party in an listed in or in (so not including lower and upper houses). is the wikidata property:

P194: legislative body

P208: executive body

required to expand {{wikidata}} template

Note: P1410 is the property "number of seats in assembly", used to record an entity's seats in legislative or executive bodies

]]

local function allpp_seats_in_eu_institutions (frame, party, institution, body_prop)

local args_input_t = get_args (frame);

local verbose = local_parameter_to_value_or_boolean (frame, 'verbose', 'boolean'); -- check verbose and make boolean

local args_t = {};

local retval = "";

if party == "THISPARTY" then -- flag used when module is called from the page of a European party; less expensive

retval = xlate_wikidata ('property', '', 'P1410', body_prop, cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no'); -- init some of the {{wikidata}} parameters with THISPARTY (only when called from the page of a European party)

elseif cfg.alliances_t[party] then

retval = xlate_wikidata ('property', cfg.alliances_t[party], 'P1410', body_prop, cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no'); -- init some of the {{wikidata}} parameters

elseif cfg.parties_t[party] then

retval = xlate_wikidata ('property', cfg.parties_t[party], 'P1410', body_prop, cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no'); -- init some of the {{wikidata}} parameters

elseif party:match ('^Q%d+$') then

retval = xlate_wikidata ('property', party, 'P1410', body_prop, cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no'); -- init some of the {{wikidata}} parameters

end

if '' == retval then -- {{wikidata}} returns empty string when not known to

if party == "THISPARTY" then -- specific error message if the module was called with THISPARTY from the wrong page

return "";

elseif not party then

return make_error_msg (substitute (cfg.error_messages_t.party_req_share)), true;

else

return make_error_msg (substitute (cfg.error_messages_t.inst_unknown_party, {institution, party})), true;

end

end

return retval;

end

--[[--------------------------< E U P P _ S U M _ S E A T S _ I N _ E U _ I N S T I T U T I O N S >------------------------------------------------------------------------

return the sum of seats for all European parties combined in an listed in (so not including lower and upper houses). is the wikidata

property:

P194: legislative body

P208: executive body

required to expand {{wikidata}} template

Note: P1410 is the property "number of seats in assembly", used to record an entity's seats in legislative or executive bodies

]]

local function eupp_sum_seats_in_eu_institutions (frame, institution, body_prop)

local sum = 0; -- init

for _, qid in pairs (cfg.parties_t) do -- loop through all parties in

local local_qid = qid; -- set the last {{wikidata}} parameter

sum = sum + xlate_wikidata ('property', local_qid, 'P1410', body_prop, cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no'); -- expand and tally

end

return sum;

end

--[[--------------------------< E U P P _ S E A T S _ I N _ E U _ I N S T I T U T I O N S >--------------------------------------------------------------------

return a number of seats of a European party (or special parameter) in an (so not including lower and upper houses).

required to expand {{wikidata}} template

Note:

  • P1410 is the property "number of seats in assembly", used to record an entity's seats in legislative or executive bodies
  • P1342 is the property "number of seats", used to record an institution's number of seats
  • P208 is the property "executive body"
  • P194 is the property "legislative body"

]]

local function eupp_seats_in_eu_institutions (frame, party, institution)

if party == "IND" and institution == "EUCO" then -- special case of independent politicians on European Council

return xlate_wikidata ('property', cfg.misc_parties_t['IND'], 'P1410', 'P208', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

elseif party == "NONE" then -- returns seats not occupied by European parties

local retval = xlate_wikidata ('property', cfg.institutions_t[institution], 'P1342', , , 'reference=no', 'preferred=no', 'raw=no', 'linked=no'); -- get number of seats in the institution

if institution == "EUCO" then -- if EUCO, use P208 and separate case to account for independent politicians

local ind = xlate_wikidata ('property', cfg.misc_parties_t['IND'], 'P1410', 'P208', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

return retval - (eupp_sum_seats_in_eu_institutions (frame, institution, cfg.body_prop_t[institution]) + ind);

else -- COR, EC, EP

return retval - eupp_sum_seats_in_eu_institutions (frame, institution, cfg.body_prop_t[institution]);

end

elseif party == "ALL" then -- returns seats occupied by all European parties combined

return eupp_sum_seats_in_eu_institutions (frame, institution, cfg.body_prop_t[institution]);

elseif party:match ('^Q%d+$') then

local party_name = cfg.rev_parties_t[party] or cfg.rev_alliances_t[party];

return allpp_seats_in_eu_institutions (frame, party_name, institution, cfg.body_prop_t[institution]);

else -- returns the number of seats occupied by one party in one institution

return allpp_seats_in_eu_institutions (frame, party, institution, cfg.body_prop_t[institution]);

end

end

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

return a share of seats of a European party (or special parameter) in an (so not including lower and upper houses).

required to expand {{wikidata}} template

Note:

  • P1410 is the property "number of seats in assembly", used to record an entity's seats in legislative or executive bodies
  • P1342 is the property "number of seats", used to record an institution's number of seats
  • P208 is the property "executive body"
  • P194 is the property "legislative body"

]]

local function eupp_seat_share_in_eu_institutions (frame, party, institution)

if party == "IND" and institution == "EUCO" then -- special case of independent politicians on European Council

return allpp_make_share_in_eu_institutions (frame, xlate_wikidata ('property', cfg.misc_parties_t['IND'], 'P1410', 'P208', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no'), institution);

elseif party == "NONE" then -- returns seats not occupied by European parties

local retval = xlate_wikidata ('property', cfg.institutions_t[institution], 'P1342', , , 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

if institution == "EUCO" then -- if EUCO, use P208 and separate case to account for independent politicians

local ind = xlate_wikidata ('property', cfg.misc_parties_t['IND'], 'P1410', 'P208', cfg.institutions_t[institution], 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

return allpp_make_share_in_eu_institutions (frame, retval - (eupp_sum_seats_in_eu_institutions (frame, institution, cfg.body_prop_t[institution]) + ind), institution);

else -- for COR, EC, EP

return allpp_make_share_in_eu_institutions (frame, retval - eupp_sum_seats_in_eu_institutions (frame, institution, cfg.body_prop_t[institution]), institution);

end

elseif party == "ALL" then -- returns seats occupied by all European parties combined

return allpp_make_share_in_eu_institutions (frame, eupp_sum_seats_in_eu_institutions (frame, institution, cfg.body_prop_t[institution]), institution);

elseif party:match ('^Q%d+$') then

party = cfg.rev_parties_t[party] or cfg.rev_alliances_t[party];

return allpp_make_share_in_eu_institutions (frame, allpp_seats_in_eu_institutions (frame, party, institution, cfg.body_prop_t[institution]), institution);

else -- returns the number of seats occupied by one party in one institution

return allpp_make_share_in_eu_institutions (frame, allpp_seats_in_eu_institutions (frame, party, institution, cfg.body_prop_t[institution]), institution);

end

end

--[[--------------------------< N P P _ S E A T S _ P E R _ R O W >------------------------------

returns the number of seats occupied by a given party (identified by a given row in the master table) in the

lower or upper house of its member state from wikidata. When not recognized or when

does not have 'that' house, returns 0

]]

local function npp_seats_per_row (frame, row, house_type)

local house_qid = "";

local national_party_qid = cfg.tab_data_t[row].national_party_qid;

if house_type == "LOWER_HOUSE" then

house_qid = cfg.tab_data_t[row].lower_house_qid; -- get the qID of a lower house

elseif house_type == "UPPER_HOUSE" then

house_qid = cfg.tab_data_t[row].upper_house_qid; -- get the qID of an upper house

end

return house_qid and xlate_wikidata ('property', national_party_qid, 'P1410', 'P194', house_qid, 'reference=no', 'preferred=no', 'raw=no', 'linked=no') or 0;

end

--[[--------------------------< E U P P _ S E A T S _ I N _ N A T I O N A L _ I N S T I T U T I O N S >------------------------------

returns the sum of seats occupied by all national parties members of a given European party in the lower or upper house of its member state.

]]

local function eupp_seats_in_national_institutions (frame, party, house_type)

local sum_seats = 0; -- init sum of seats of European party's member parties in house_type

for row, _ in ipairs (cfg.tab_data_t) do

if party == "THISPARTY" then -- if called from the page of a European party

local thisparty_qid = mw.wikibase.getEntityIdForCurrentPage(); -- get party qiD

if thisparty_qid == cfg.tab_data_t[row]['european_party_qid'] then

sum_seats = sum_seats + npp_seats_per_row (frame, row, house_type); -- increase sum_seats

end

elseif party == "ALL" or party == "NONE" then -- not accepted parameters

return make_error_msg (substitute (cfg.error_messages_t.all_none_unavailable, {party}));

elseif party == "IND" then -- not accepted parameter

return make_error_msg (substitute (cfg.error_messages_t.ind_only_euco));

elseif party:match ('^Q%d+$') then -- if party name is a qID

if party == cfg.tab_data_t[row]['european_party_qid'] then

sum_seats = sum_seats + npp_seats_per_row (frame, row, house_type); -- increase sum_seats

end

else --for any other party name (already approved via party_type validation)

if party == cfg.tab_data_t[row]['european_party'] then

sum_seats = sum_seats + npp_seats_per_row (frame, row, house_type); -- increase sum_seats

end

end

end

return sum_seats;

end

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

return a share of seats occupied by one or more entities listed in or , or by none of them, in an (so not including lower and upper houses).

required to expand {{wikidata}} template

]]

local function eupp_seat_share_in_national_institutions (frame, party, institution)

return round (100 * eupp_seats_in_national_institutions (frame, party, institution) / total_house_seats (frame, institution)); -- return share of seats by calling seat_share()

end

--[[--------------------------< N P P _ S E A T S _ A N D _ S E A T _ S H A R E _ I N _ N A T I O N A L _ I N S T I T U T I O N S >------------------------------------------------

returns the sum or share of seats occupied by a national party in the lower or upper house of its member state.

]]

local function npp_seats_and_seat_share_in_national_institutions (frame, party, house_type, data)

local args_t = get_args (frame); -- get arguments; empty string or whitespace positional parameters set to nil

local party_qid = "";

local constituency = local_parameter_to_value_or_boolean (frame, 'constituency'); -- argument overriding the house's number of seats

if party == "THISPARTY" then

party_qid = mw.wikibase.getEntityIdForCurrentPage();

elseif party:match ('^Q%d+$') then

party_qid = party;

end

--[=[ data validation for party_qid ]=]

if party_qid == '' then

return make_error_msg (cfg.error_messages_t.no_qid, template_name); -- yep, abandon with error message

end

--[=[ get house_qid from party qid and house_seats ]=]

local member_state_qid = allpp_get_data (frame, party_qid, cfg.data_prop_t.country, 'raw-noref');

local house_qid = house_qid_from_member_state_qid (house_type, member_state_qid);

local npp_seats = house_qid and xlate_wikidata ('property', party_qid, 'P1410', 'P194', house_qid, 'reference=no', 'preferred=no', 'raw=no', 'linked=no') or 0;

local house_seats = house_qid and xlate_wikidata ('property', house_qid, 'P1342', , , 'reference=no', 'preferred=no', 'raw=no', 'linked=no') or 0;

if data == "seats" then

return npp_seats;

elseif data == "seat share" then

if constituency then

return round (100 * npp_seats / constituency);

else

return round (100 * npp_seats / house_seats);

end

elseif data == "house seats" then

return house_seats;

end

end

--[[--------------------------< A L L P P _ C O M P O S I T I O N _ B A R >------------------------------------------------

this function does whatever it is that {{composition bar}} does

implements {{EUPP composition bar}}

{{EUPP composition bar|||width=|percent=yes|reference=yes|bar-color=|background-color=|border=}}

]]

local function allpp_composition_bar (frame, party, institution, party_type)

local args_t = get_args (frame); -- get arguments; empty string or whitespace positional parameters set to nil

local verbose = local_parameter_to_value_or_boolean (frame, 'verbose', 'boolean'); -- check verbose and make boolean

local percentage = local_parameter_to_value_or_boolean (frame, 'percent', 'boolean'); -- check percent and make boolean

local reference = local_parameter_to_value_or_boolean (frame, 'reference', 'boolean'); -- check reference and make boolean

local width = local_parameter_to_value_or_boolean (frame, 'width'); -- must be a number, or number with unit suffix: 'px', '%', 'em'; whitespace not allowed

local constituency = local_parameter_to_value_or_boolean (frame, 'constituency'); -- argument overriding the house's number of seats

local institution_seats = 0;

local party_seats = 0;

local background_color = local_parameter_to_value_or_boolean (frame, 'background-color');

local border = local_parameter_to_value_or_boolean (frame, 'border');

if width and not validate_width (width) then

return make_error_msg (substitute (cfg.error_messages_t.parameter_invalid, {width}), template_name); -- yep, abandon with error message

end

--[=[ prepare arguments for composition bar ]=]

if party_type == "european_entity" then

if institution == "LOWER_HOUSE" or institution == "UPPER_HOUSE" then

party_seats = eupp_seats_in_national_institutions (frame, party, institution); -- get sum of seats occupied by members of a European party

institution_seats = total_house_seats(frame, institution); -- get total seats of lower or upper houses

else

party_seats = eupp_seats_in_eu_institutions (frame, party, institution); -- get total seats in occupied by

institution_seats = institution_size (frame, institution); -- get total seats in

end

elseif party_type == "national_party" then

if institution == "LOWER_HOUSE" or institution == "UPPER_HOUSE" then

party_seats = npp_seats_and_seat_share_in_national_institutions (frame, party, institution, "seats");

institution_seats = constituency or npp_seats_and_seat_share_in_national_institutions (frame, party, institution, "house seats")

elseif institution == "EP" then

local member_state_qid = allpp_get_data (frame, party, cfg.data_prop_t.country, 'raw-noref');

local has_error;

party_seats, has_error = allpp_seats_in_eu_institutions (frame, party, institution, cfg.body_prop_t[institution]);

if has_error then

return party_seats; -- has an error message

end

institution_seats = constituency or member_state_delegation_size (frame, member_state_qid); -- get total seats in

else

local has_error;

party_seats, has_error = allpp_seats_in_eu_institutions (frame, party, institution, cfg.body_prop_t[institution]);

if has_error then

return party_seats; -- has an error message

end

institution_seats = constituency or institution_size (frame, institution); -- get total seats in

end

end

local color = local_parameter_to_value_or_boolean (frame, 'bar-color') or allpp_get_data (frame, party, cfg.data_prop_t.colour); -- get color associated with ; |bar-color= overrides wikidata

if == party_seats or == institution_seats then

if verbose then

return make_error_msg (substitute (cfg.error_messages_t.no_data_short));

else

return "";

end

elseif party_type == "european_entity" and (institution == "LOWER_HOUSE" or institution == "UPPER_HOUSE") then

return xlate_composition_bar (frame, party_seats, institution_seats, color, width, percentage, background_color, border);

else

return xlate_composition_bar (frame, party_seats, institution_seats, color, width, percentage, background_color, border) .. ((reference and allpp_seats_ref (frame, party, institution)) or '');

end

end

--==========================< S E C T I O N _ 5 : M A I N _ & _ T E S T _ F U N C T I O N S >=======================

--[[--------------------------< M A I N >----------------------------------------------------------------------

implements {{EUPP data}} and {{political party data}} (the templates hard-code the information)

carries out input error detection, reporting, and function dispatching

Module called by as follows:

{{EUPP data||||width=|percent=yes|reference=yes|bar-color=|background-color=|border=}}

{{Political party data||||width=|percent=yes|reference=yes|bar-color=|background-color=|border=}}

where is:

  • seats
  • seat share
  • seat composition bar
  • colour
  • individual members
  • acronym
  • country
  • name
  • public funding
  • website
  • foundation date

]]

local function main(frame)

local template_name = frame:getParent():getTitle()

local args_t = get_args (frame); -- get arguments; empty string or whitespace positional parameters set to nil

local party_type = args_t[1] and args_t[1]:lower(); -- force to lower case

local data_type = args_t[2] and args_t[2]:lower(); -- force to lower case

local party = args_t[3] and args_t[3]:upper(); -- force to upper case

local institution = args_t[4] and args_t[4]:upper(); -- force to upper case

local party_qid = "";

local ref = "";

--[=[ translate arguments ]=]

party_type = xlate (party_type);

data_type = xlate (data_type);

party = xlate (party);

institution = xlate (institution);

--[=[ prepare arguments ]=]

local reference = local_parameter_to_value_or_boolean (frame, 'reference', 'boolean'); -- make a boolean

institution = harmonise_lower_upper_house_name (institution); -- "ms-lower-house" and "lower" are turned to "lower_house" (same for upper house)

party = harmonise_lower_upper_house_name (party); -- in case institution is entered as party

if cfg.institutions_t[party] or party == "LOWER_HOUSE" or party == "UPPER_HOUSE" then -- if the name of the party is actually the institution, then party is "thisparty"

institution = party;

party = "THISPARTY";

end

if not party then -- if party is missing, then it is "thisparty"

party = "THISPARTY";

end

--[=[ data validation party type ]=]

if party_type == "european_entity" and not (cfg.parties_t[party] or cfg.alliances_t[party] or cfg.rev_parties_t[party] or cfg.rev_alliances_t[party] or party == "ALL" or party == "NONE" or party == "IND" or party == "THISPARTY") then

return make_error_msg (substitute (cfg.error_messages_t.not_valid_eupp_parameter, {party}), template_name);

elseif party_type == "european_entity" and party == "THISPARTY" and not (cfg.rev_parties_t[mw.wikibase.getEntityIdForCurrentPage()] or cfg.rev_alliances_t[mw.wikibase.getEntityIdForCurrentPage()]) then

return make_error_msg (substitute (cfg.error_messages_t.thisparty), template_name);

elseif party_type == "national_party" and (cfg.parties_t[party] or cfg.alliances_t[party] or cfg.rev_parties_t[party] or cfg.rev_alliances_t[party] or party == "ALL" or party == "NONE" or party == "IND" or (party == "THISPARTY" and not mw.wikibase.getEntityIdForCurrentPage())) then

return make_error_msg (substitute (cfg.error_messages_t.not_valid_npp, {party}), template_name);

end

--[=[ data validation institution ]=]

local is_valid = false;

is_valid = validate_institution (institution, template_name);

if true ~= is_valid then -- boolean true when valid; error message else

return is_valid; -- yep, abandon with error message

end

--[=[ function dispatching ]=]

--[=[ send to seat functions ]=]

if data_type == "seats" then -- we look for a seat number

if not institution then -- institution is required

return make_error_msg (cfg.error_messages_t.missing_inst, template_name);

end

if true == reference then

ref = allpp_seats_ref (frame, party, institution);

end

if party_type == "european_entity" then -- the party is a European party or European alliance (specified by the calling template, not by the user)

if institution == "LOWER_HOUSE" or institution == "UPPER_HOUSE" then -- we look for seats aross lower or upper houses

return eupp_seats_in_national_institutions (frame, party, institution) .. ref; -- return the number of seats by calling eupp_seats_in_national_institutions

else -- we look for seats in a European institution

return eupp_seats_in_eu_institutions (frame, party, institution) .. ref; -- return the number of seats by calling eupp_seats_in_eu_institutions

end

elseif party_type == "national_party" then -- the party is a national party

if institution == "LOWER_HOUSE" or institution == "UPPER_HOUSE" then -- we look for seats in a lower or upper house

return npp_seats_and_seat_share_in_national_institutions (frame, party, institution, "seats") .. ref; -- return the number of seats by calling ...

else -- we look for seats in a European institution

return allpp_seats_in_eu_institutions (frame, party, institution, cfg.body_prop_t[institution]) .. ref; -- return the number of seats by calling allpp_seats_in_eu_institutions

end

else

return make_error_msg (substitute (cfg.error_messages_t.unknown_party_type, {party_type}), template_name);

end

--[=[ send to seat share functions ]=]

elseif data_type == "seat share" then

if not institution then -- institution is required

return make_error_msg (cfg.error_messages_t.missing_inst, template_name);

end

if party_type == "european_entity" then

if institution == "LOWER_HOUSE" or institution == "UPPER_HOUSE" then

return eupp_seat_share_in_national_institutions (frame, party, institution);

else

return eupp_seat_share_in_eu_institutions (frame, party, institution);

end

elseif party_type == "national_party" then

if institution == "LOWER_HOUSE" or institution == "UPPER_HOUSE" then

return npp_seats_and_seat_share_in_national_institutions (frame, party, institution, "seat share");

else

return allpp_make_share_in_eu_institutions (frame, allpp_seats_in_eu_institutions (frame, party, institution, cfg.body_prop_t[institution]), institution); -- return share of seats by calling seat_share()

end

else

return make_error_msg (substitute (cfg.error_messages_t.unknown_party_type, {party_type}), template_name);

end

--[=[ send to composition bar functions ]=]

elseif data_type == "seat composition bar" then

if not institution then -- institution is required

return make_error_msg (cfg.error_messages_t.missing_inst, template_name);

end

return allpp_composition_bar (frame, party, institution, party_type);

--[=[ send to get_data functions ]=]

elseif data_type == "acronym" then

return allpp_get_data (frame, party, cfg.data_prop_t.acronym);

elseif data_type == "color" then

return allpp_get_data (frame, party, cfg.data_prop_t.colour);

elseif data_type == "country" then

return allpp_get_data (frame, party, cfg.data_prop_t.country, 'linked');

elseif data_type == "foundation date" then

return allpp_get_data (frame, party, cfg.data_prop_t.foundation_date);

elseif data_type == "foundation year" then

return mw.getContentLanguage():formatDate("Y",allpp_get_data (frame, party, cfg.data_prop_t.foundation_date, 'raw'));

elseif data_type == "individual members" then

return allpp_get_data (frame, party, cfg.data_prop_t.individual_members);

elseif data_type == "individual members date" then

return allpp_get_data (frame, party, 'individual members date');

elseif data_type == "individual members year" then

return mw.getContentLanguage():formatDate("Y",allpp_get_data (frame, party, 'individual members date', 'raw'));

elseif data_type == "label" then

return allpp_get_data (frame, party, 'label');

elseif data_type == "official name" then

return allpp_get_data (frame, party, cfg.data_prop_t.official_name);

elseif data_type == "parliamentary group" then

return allpp_get_data (frame, party, cfg.data_prop_t.parliamentary_group, 'linked');

elseif data_type == "public funding" then

return allpp_get_data (frame, party, cfg.data_prop_t.public_funding);

elseif data_type == "website" then

return allpp_get_data (frame, party, cfg.data_prop_t.website);

else

return make_error_msg (substitute (cfg.error_messages_t.unknown_data_type, {data_type}), template_name);

end

end

--[[--------------------------< E U _ I N S T I T U T I O N _ S E A T S >----------------------------------------------------------------------

]]

local function eu_institution_seats (frame)

local template_name = frame:getParent():getTitle()

local args_t = get_args (frame); -- get arguments; empty string or whitespace positional parameters set to nil

local institution = args_t[1] and args_t[1]:upper(); -- force to upper case

institution = xlate (institution);

institution = harmonise_lower_upper_house_name (institution); -- "ms-lower-house" and "lower" are turned to "lower_house" (same for upper house)

--[=[ data validation institution ]=]

local is_valid = false;

is_valid = validate_institution (institution, template_name);

if true ~= is_valid then -- boolean true when valid; error message else

return is_valid; -- yep, abandon with error message

end

--[=[ return value ]=]

if institution == 'LOWER_HOUSE' or institution == 'UPPER_HOUSE' then

return total_house_seats (frame, institution);

else

return institution_size (frame, institution);

end

end

--[[--------------------------< T E S T _ W I K I D A T A _ E N T R I E S >----------------------------------------------------------------------

]]

local function test_wikidata_entries (frame) -- to test calls and functions, for verification purposes

local wikidata_error = "Wikidata entries error(s):";

for row, _ in ipairs (cfg.tab_data_t) do

local national_party_qid = cfg.tab_data_t[row].national_party_qid;

local national_party_name = "";

if not cfg.tab_data_t[row].national_party then

national_party_name = cfg.tab_data_t[row].national_party_english;

else

national_party_name = cfg.tab_data_t[row].national_party;

end

local lower_house_qid = cfg.tab_data_t[row].lower_house_qid;

local lower_house_name = cfg.tab_data_t[row].lower_house;

local upper_house_qid = cfg.tab_data_t[row].upper_house_qid;

local upper_house_name = cfg.tab_data_t[row].upper_house;

local retval_lower = xlate_wikidata ('property', national_party_qid, 'P1410', 'P194', lower_house_qid, 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

local retval_upper = xlate_wikidata ('property', national_party_qid, 'P1410', 'P194', upper_house_qid, 'reference=no', 'preferred=no', 'raw=no', 'linked=no');

if '' == retval_lower then

wikidata_error = wikidata_error .. " [https://www.wikidata.org/wiki/" .. national_party_qid .. " " .. national_party_name .. "] (" .. lower_house_name .. ")";

end

if '' == retval_upper then

wikidata_error = wikidata_error .. " [https://www.wikidata.org/wiki/" .. national_party_qid .. " " .. national_party_name .. "] (" .. upper_house_name .. ")";

end

end

if wikidata_error == "Wikidata entries error(s):" then

wikidata_error = "Wikidata entries: all good!";

end

return wikidata_error;

end

--[[--------------------------< T E S T >----------------------------------------------------------------------

]]

local function test (frame) -- to test calls and functions, for verification purposes

local args_t = get_args (frame); -- get arguments; empty string or whitespace positional parameters set to nil

local parameter = args_t['parameter'] and args_t['parameter']:lower(); -- force to upper case

local outcome = args_t['outcome'] and args_t['outcome']:lower(); -- force to upper case

local localised_parameter = xlate_param (parameter); -- translate given parameter from English into local language

local argument = (args_t[tostring(localised_parameter)] and args_t[tostring(localised_parameter)]:lower()) or (args_t[parameter] and args_t[parameter]:lower()); -- get argument (in local language) matching parameter in local language

if outcome == 'boolean' then

if nil == argument then -- if no argument, return no

argument = false;

else

argument = xlate (argument); -- translate argument into English

argument = 'yes' == argument; -- turn argument into boolean

end

elseif argument:match ('^#') then

argument = argument.gsub(argument, "#", "#");

end

return argument;

--return "parameter: " .. parameter .. " - outcome: " .. tostring(outcome) .. " - localised parameter: " .. localised_parameter .. " - argument: " .. tostring(argument);

end

local function test2 (frame)

return cfg.this_project_language;

end

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

]]

return {

main = main,

eu_institution_seats = eu_institution_seats,

test = test,

test2 = test2,

test_wikidata_entries = test_wikidata_entries,

-- xlate_wikidata = xlate_wikidata

}