Open main menu
Home
Random
Donate
Recent changes
Special pages
Community portal
Preferences
About Stockhub
Disclaimers
Search
User menu
Talk
Contributions
Create account
Log in
Editing
Module:IATA and ICAO code/sandbox
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
require('strict'); local getArgs = require ('Module:Arguments').getArgs; local lang = mw.language.getContentLanguage(); -- used for date formatting and validation local namespace = mw.title.getCurrentTitle().namespace; -- used for categorization local master = mw.loadData("Module:IATA and ICAO code/data/sandbox") local IATA_airport = master.IATA local ICAO_airport = master.ICAO local wikilink_label = master.destination local categories = master.i18n['categories']; local date_ref_text = master.i18n['date_ref_text']; local err_msg = master.i18n['err_msg']; local fall_back_wiki = master.i18n['fall_back_wiki']; local keywords = master.i18n['keywords']; local presentation = master.i18n['presentation']; local config = master.config; local function count(frame) local count = 0 for i, v in ipairs(frame.args) do count=count+1 end return count end --[[--------------------------< I S _ S E T >------------------------------------------------------------------ Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string. ]] local function is_set( var ) return not (var == nil or var == ''); end --[[--------------------------< S U B S T I T U T E >---------------------------------------------------------- Populates numbered arguments in a message string using an argument table. ]] local function substitute (msg, args) return args and mw.message.newRawMessage (msg, args):plain() or msg; end --[[--------------------------< M A K E _ E R R _ M S G >------------------------------------------------------ assembles a complete error message and then wraps it in appropriate presenation markup ]] local function make_err_msg (msg, args) return substitute (presentation['err_msg'], substitute (msg, args)); end --[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- Dates must be real. Returns true if date is a real date; false else. Done this way because: mw.language.getContentLanguage():formatDate('Y-m-d', '2018-02-31') should return an error but instead returns 2018-03-03 ]] local function is_valid_date (date) local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -- number of days in each month local month_length; local year, month, day; year, month, day = date:match ('(%d%d%d%d)%-(%d%d)%-(%d%d)'); -- extract year, month, and day parts from date if (year < lang:formatDate ('Y', 'now')) or (year > lang:formatDate ('Y', 'now+2year')) then return false; end month = tonumber (month); if (1 > month) or (12 < month) then -- check month return false; end if (2==month) then -- if February month_length = 28; -- then 28 days unless if (0==(year%4) and (0~=(year%100) or 0==(year%400))) then -- a leap year month_length = 29; -- in which case: 29 days in February end else month_length=days_in_month[month]; -- else month length from table end day = tonumber (day); if (1 > day) or ( month_length < day) then -- check day return false; end return true; -- good day end --[[-------------------------< M A K E _ D A T E _ T Y P E _ S T R I N G >------------------------------------ decodes begin, resume, end service date type and date stamps and returns human readable text. Format is: <type><date> where: <type> is one of the three-character keywords: beg - new service begins res - service resumes after an interruption end - service ends <date> is a singe date or date-range similar in format to ISO 8601: YYYY-MM-DD - a single YMD date may be begin, resume, or end date YYYY-MM-DD/YYYY-MM-DD - a date range where the first date (left) is a start or resume date and the second (right) is an end date returns nil and an error message if: type keyword is not recognized either date is invalid - dates must be real begin or resume date falls on or after end date - events must begin/resume before they end event dates that have already occurred (today's date follows a begin/resume/end date) are quitely muted. When this occurs the function returns only an end date (if a date range and that event is still in the future). The function will add a hidden category to articles with muted begin/resume/end dates. ]] local function make_date_type_string (date_type, date, date2, ref, df) local result = {}; local cat = ''; local today = lang:formatDate ('Y-m-d', 'today'); -- system date in ymd format as a text string if not is_set (date_type) then -- if not set then this airport code isn't dated return '', nil, ''; -- return empty string, no error message, empty string for category end if not is_valid_date (date) then -- dates must be valid return nil, substitute (err_msg['bad_date'], date); end if date2 then -- if date is a range if 'end' == date_type then return nil, err_msg['bad_end']; end if not is_valid_date (date2) then -- dates must be valid return nil, substitute (err_msg['bad_date'], date2); end if date >= date2 then -- start/resume dates must precede end dates return nil, substitute (err_msg['bad_date_order'], {date, date2}); end end if date < today then -- if date has passed date = nil; -- quietly hide expired dates cat = substitute (presentation['maint_msg'], categories['expired']) -- (but categorize) end if date2 then -- if date is a range if date2 < today then -- if date has passed date2 = nil; -- quietly hide expired dates cat = substitute (presentation['maint_msg'], categories['expired']) -- (but categorize) end end if date then -- define formatting strings for lang:formatDate() if keywords['dmy'] == df then df = 'j F Y'; elseif keywords['mdy'] == df then df = 'F j, Y'; else df = 'Y-m-d'; -- yeah, sort of pointless, but simple end date = lang:formatDate (df, date); -- reformat ymd to selected format according to |df= if date2 then date2 = lang:formatDate (df, date2); -- reformat ymd to selected format according to |df= end end date_type = date_type:lower(); -- make it case insensitive if keywords['beg'] == date_type then if date and date2 then -- a valid begin/end date range table.insert (result, substitute (date_ref_text['beg_end'], {date, date2, ref})); elseif date then -- a valid begin date table.insert (result, substitute (date_ref_text['begs'], {date, ref})); elseif date2 then -- date may be nil here because it has already passed table.insert (result, substitute (date_ref_text['ends'], {date2, ref})); -- so render only the end date end elseif keywords['res'] == date_type then if date and date2 then -- a valid begin/end date range table.insert (result, substitute (date_ref_text['res_end'], {date, date2, ref})); elseif date then -- a valid begin date table.insert (result, substitute (date_ref_text['ress'], {date, ref})); elseif date2 then -- date may be nil here because it has already passed table.insert (result, substitute (date_ref_text['ends'], {date2, ref})); -- so render only the end date end elseif keywords['end'] == date_type then if date then table.insert (result, substitute (date_ref_text['ends'], {date, ref})); -- render the end date end end if 0 ~= namespace then cat = '' -- categorize articles in mainspace only end if 0 ~= #result then -- when there is date text, return table.concat (result), nil, cat; -- return formatted date string, no error message, and category (if mainspace) else return '', nil, cat; -- return empty string for concatenation, no error message, and category (if mainspace) end end --[[--------------------------< M A K E _ A R T I C L E _ L I N K S >------------------------------------------ makes article links for local wiki and, if iwiki_article is set, an interwiki link to en.wiki. ]] local function make_article_links (wiki_article, label, iwiki_article, date_type, cat) if iwiki_article then -- if we got an article name from wikidata return table.concat ({ '[[', -- open local language wikilink wiki_article, -- local target article '|', label, -- local destination city ']]', -- close local ' [[:', -- open interwiki link; fall_back_wiki, -- specify the wiki ':', -- separator iwiki_article, -- interwiki target article '|', substitute (presentation['inter_wiki'], {wiki_article, fall_back_wiki}), -- tool tip; ']]', -- close interwiki link date_type, -- add dates cat -- and categories if necessary }); else return table.concat ({'[[', wiki_article, '|', label, ']]', date_type, cat}); -- make local wiki wikilink end end --[=[-------------------------< C O M P >--------------------------------------------------------------------- Comparison funtion for table.sort() compares wikilink labels [[wikilink target|wikilink label]] ]=] local function comp (a, b) local a_label = a:match ('^[^|]+|([^%]]+)%]%]'):lower(); -- extract the label from a local b_label = b:match ('^[^|]+|([^%]]+)%]%]'):lower(); -- extract the label from b return a_label < b_label; end --[=[--------------------------< M A I N >-------------------------------------------------------------------- returns a wikilink to the en.wiki article that matches the IATA or ICAO code provided in the call example of data: {'North America','USA','ABE','KABE','Lehigh Valley International Airport','Allentown/Bethlehem'}, {{#invoke:IATA and ICAO code|main|ABE}} returns [[Lehigh Valley International Airport|Allentown/Bethlehem]] or an error message When there are multiple codes, returns comma separated, alpha ascending list of wikilinks or an error message. ]=] local function main(frame) local args = getArgs(frame); local results = {}; local code; local en_wiki_article; local date_type; local date, date2; local message; local ref = ''; local cat = ''; if args.ill then fall_back_wiki = args.ill; end for _, value in ipairs (args) do value = mw.text.trim (value); if value:match ('^(%a%a%a%a?) *: *(%a%a%a) *(%d%d%d%d%-%d%d%-%d%d) */ *(%d%d%d%d%-%d%d%-%d%d) *(.*)') then code, date_type, date, date2, ref = value:match ('^(%a%a%a%a?) *: *(%a%a%a) *(%d%d%d%d%-%d%d%-%d%d) */ *(%d%d%d%d%-%d%d%-%d%d) *(.*)'); elseif value:match ('^(%a%a%a%a?) *: *(%a%a%a) *(%d%d%d%d%-%d%d%-%d%d) *(.*)') then code, date_type, date, ref = value:match ('^(%a%a%a%a?) *: *(%a%a%a) *(%d%d%d%d%-%d%d%-%d%d) *(.*)'); elseif value:match ('^%a%a%a%a?$') then code = value:match ('^%a%a%a%a?$'); else return make_err_msg (err_msg['malformed_param'], value); end ref = mw.text.trim (ref); -- remove extraneous white space; if ref is only white space, makes empty string if '' ~= ref then if not ref:match ('^\127[^\127]*UNIQ%-%-ref%-%x+%-QINU[^\127]*\127') then return make_err_msg (err_msg['extra_ref_text'], ref); end end date_type, message, cat = make_date_type_string (date_type, date, date2, ref, args.df); if message then -- if an error message, abandon return make_err_msg (err_msg['non_specific_error'], message); end if date_type then -- nil when end date type has expired code = code:upper(); -- force code to uppercase if IATA_airport[code] and is_set (IATA_airport[code][1]) then if IATA_airport[code][2] then en_wiki_article = mw.wikibase.getEntity (IATA_airport[code][2]):getSitelink (fall_back_wiki .. 'wiki'); end table.insert (results, make_article_links (IATA_airport[code][1], wikilink_label[code], en_wiki_article, date_type, cat)); elseif ICAO_airport[code] and is_set (ICAO_airport[code][1]) then if ICAO_airport[code][2] then en_wiki_article = mw.wikibase.getEntity (ICAO_airport[code][2]):getSitelink (fall_back_wiki .. 'wiki'); end table.insert (results, make_article_links (ICAO_airport[code][1], wikilink_label[code], en_wiki_article, date_type, cat)); else return make_err_msg (err_msg['data_missing'], code); end end date_type = nil; -- clear so we can reuse these date = nil; date2 = nil; en_wiki_article = nil; ref = ''; end table.sort (results, comp); -- sort in ascending alpha order local i = 1; while i<#results do -- check for and remove duplicates if results[i] == results[i+1] then -- compare table.remove (results, i); -- remove the duplicate at results[i]; do not bump i else i = i + 1; -- not the same so bump i end end if master.config.bullet_enable and master.config.bullet_qty <= #results then table.insert (results, 1, ''); -- insert empty string for following table.concat() local out = {'<div class="div-col columns column-width" style="-moz-column-width:'} -- begin opening div tag table.insert (out, master.config.bullet_col_width); -- add various column width style attributes table.insert (out, 'em; -webkit-column-width:'); table.insert (out, master.config.bullet_col_width); -- add various column width style attributes table.insert (out, 'em; column-width:') table.insert (out, master.config.bullet_col_width); -- add various column width style attributes table.insert (out, 'em;">'); -- close opening div tag table.insert (out, table.concat (results, '\n*')); -- concatenate results and put the result in the div table.insert (out, '</div>') -- close the div return table.concat (out); -- and done end return table.concat (results, ', '); -- else make a comma separated list end --[[--------------------------< D U P L I C A T E _ C H E C K >------------------------------------------------ This function looks at IATA and ICAO code in Module:IATA and ICAO code/data. It attempts to locate invalid (by length) codes and attempts to detect duplicate codes. 1 2 3 4 {'North America','USA','ABE','KABE','Lehigh Valley International Airport','Allentown/Bethlehem'}, ]] local function duplicate_check () local iata_count_table = {}; local icao_count_table = {}; local _master = master._master local iata = {} local icao = {} for i, v in ipairs(_master) do -- create tables from master if '' ~= v[3] then -- iata codes TODO: length checking if not iata[v[3]] then iata[v[3]] = 1; -- state that this is the first time we've seen this code else iata[v[3]] = iata[v[3]] + 1; -- bump the count for this code end end if '' ~= v[4] then -- iaco codes TODO: length checking if not icao[v[4]] then icao[v[4]] = 1; -- state that this is the first time we've seen this code else icao[v[4]] = icao[v[4]] + 1; -- bump the count for this code end end end for k, v in pairs (iata) do if 1 < v then table.insert (iata_count_table, table.concat ({k, ' (', v, 'Γ)'})) end end for k, v in pairs (icao) do if 1 < v then table.insert (icao_count_table, table.concat ({k, ' (', v, 'Γ)'})) end end local iata_msg = ''; -- error messages go here local icao_msg = ''; if 0 ~= #iata_count_table then iata_msg = make_err_msg (err_msg['extra_iata'], table.concat (iata_count_table, ', ')) -- iata_msg = table.concat ({'<span style=\"font-size:100%; font-style:normal;\" class=\"error\">error: more than one IATA code:<br /> ', table.concat (iata_count_table, ', '), '</span>'}) end if 0 ~= #icao_count_table then icao_msg = make_err_msg (err_msg['extra_iata'], table.concat (icao_count_table, ', ')) -- icao_msg = table.concat ({'<span style=\"font-size:100%; font-style:normal;\" class=\"error\">error: more than one ICAO code:<br /> ', table.concat (icao_count_table, ', '), '</span>'}) end if (0 ~= #iata_count_table) or (0 ~= #icao_count_table) then -- TODO find a better way of doing this return table.concat ({iata_msg, '<br /><br />', icao_msg}) end return 'ok'; end --[[--------------------------< M O D U L E E X P O R T S >------------------------------------------------- ]] return { count = count, duplicate_check = duplicate_check, main = main, };
Summary:
Please note that all contributions to Stockhub may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Stockhub:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Template used on this page:
Module:IATA and ICAO code/sandbox/doc
(
edit
)