Module:Category described in year

require('strict')

--========================================================================== --                            Local functions --==========================================================================

local function addOrd( i ) --12 -> 12th, etc.	if tonumber(i) then local s = tostring(i) local tens = string.match(s, '1%d$') local ones = string.match(s,  '%d$') if    tens        then return s..'th' elseif ones == '1' then return s..'st' elseif ones == '2' then return s..'nd' elseif ones == '3' then return s..'rd' elseif ones ~= nil then return s..'th' end end return '' end

local function isNilOrEmpty( thing ) return (thing == nil or thing == '') end

local p = {}

--========================================================================== --                           External function --==========================================================================

function p.autodetect( frame ) local conf = require( 'Module:Category described in year/conf' ) --configuration module local commonsLink = require('Module:Commons link') local currentTitle = mw.title.getCurrentTitle local parentArg = frame:getParent.args[1] --accept 1 unnamed category parameter if not in category namespace; required for testing/doc/etc. purposes local header = ' ' --header template(s), nav bar, and category description text; whitespace-initialized for convenience local nav = nil local portal = nil --for local commons = nil --for local wikispecies = nil --for local description = nil local toc = nil local categories = {} local trackingCats = { [1] = '', --placeholder for [2] = '', --placeholder for }	local outString = nil local bConfError = false --prelim namespace/title determination local currCat = nil local currQID = nil if currentTitle.namespace == 14 then --category namespace currCat = currentTitle.text --without namespace nor interwiki prefixes currQID = mw.wikibase.getEntityIdForCurrentPage else if parentArg then currCat = mw.ustring.gsub(parentArg, 'Category:', '') currQID = mw.wikibase.getEntityIdForTitle('Category:'..currCat) else --currQID & currCat both nil if currentTitle.fullText ~= 'Template:Category described in year' then --ignore self... trackingCats[2] = '' --missing a category parameter outside category namespace end end end --find commons & wikispecies link(s); produce and/or  template(s) if currQID then if commonsLink._hasGallery(currQID) or commonsLink._hasCategory(currQID) then commons = frame:expandTemplate{ title = 'Commons and category', args = { qid=currQID }} end local currEntity = mw.wikibase.getEntity(currQID) if currEntity then --check "Other sites" sitelinks for Wikispecies local currSiteLinks = currEntity.sitelinks if currSiteLinks then local currSpeciesWiki = currEntity.sitelinks.specieswiki if currSpeciesWiki then local currSpeciesWikiTitle = currSpeciesWiki.title if currSpeciesWikiTitle then wikispecies = frame:expandTemplate{ title = 'Wikispecies', args = { currSpeciesWikiTitle } } end	end	end	end	end --======================================================================	--                                Main --======================================================================	if currCat then --determine current/related/adjacent cats' properties/vars/etc. local currGroup = mw.ustring.match(currCat, '^([%w ]+) described in') --Bacteria/Plants/etc. if isNilOrEmpty(currGroup) then currGroup = mw.ustring.match(currCat, '^([%w ]+) by year of formal description') end if conf[currGroup] == nil then conf[currGroup] = conf['Default'] end --default to Default local currYDCF = nil --possible future values: year/decade/century/formal local currYear = mw.ustring.match(currCat, 'described in (%d%d%d%d)$') local currDeca = mw.ustring.match(currCat, 'described in the (%d%d%d%d)s$') --deprecated local currCent = mw.ustring.match(currCat, 'described in the (%d+)[snrt][tdh] century$') local currFrml = mw.ustring.match(currCat, 'by year of (formal) description$') local parentCent = nil --used with currYear local minYear = tonumber(conf[currGroup].minyear) if minYear == nil or 		 (minYear and (minYear <= 1700 or minYear >= 2000)) then minYear = 1758 --default to 1758 per ICZN Art. 5		end if currYear then currYDCF = 'year' if mw.ustring.match(currYear, '^%d%d00') then --1900 in 19th century parentCent = mw.ustring.match(currYear, '^%d%d') else --1901 in 20th century parentCent = 1 + mw.ustring.match(currYear, '^%d%d') end elseif currDeca then currYDCF = 'decade' bConfError = true trackingCats[2] = '' --invalid decade-parent (deprecated) elseif currCent then currYDCF = 'century' elseif currFrml then currYDCF = 'formal' else bConfError = true trackingCats[2] = '' --invalid category name end --conf error checkng (missing keys) --Numeric sortkeys are unfortunately grouped together under "0-9". --Check phab T203355 (Magic word to force category number headings instead of 0-9). if bConfError == false then if conf[currGroup] == nil then bConfError = true trackingCats[2] = '' --group (Bacteria/Plants/etc.) key missing from conf elseif conf[currGroup][currYDCF] == nil then bConfError = true trackingCats[2] = '' --year/century/formal key missing else if conf[currGroup][currYDCF].description == nil then bConfError = true trackingCats[2] = '' --description key missing end if conf[currGroup][currYDCF].parent1 == nil then bConfError = true trackingCats[2] = '' --parent key missing end end end if bConfError == false then --produce portal if currGroup == 'Fossil taxa' or currGroup == 'Fossil parataxa' then portal = frame:expandTemplate{ title = 'Portal', args = { 'Paleontology' } } end --produce description, evaluate %variables% description = conf[currGroup][currYDCF].description if mw.ustring.match(description, '%%year%%') then if currYear then description = mw.ustring.gsub(description, '%%year%%', currYear) --"2011" else description = mw.ustring.gsub(description, '%%year%%', 'this year') end end if mw.ustring.match(description, '%%century%%') then if currCent then description = mw.ustring.gsub(description, '%%century%%', addOrd(currCent)) --"21st" else description = mw.ustring.gsub(description, '%%century%%', 'this century') end end --produce cats & navs local iparent = 1 local parenti = 'parent'..iparent local sortkeyi = 'sortkey'..iparent while conf[currGroup][currYDCF][parenti] do				local parent = conf[currGroup][currYDCF][parenti] local sortkey = conf[currGroup][currYDCF][sortkeyi] --========================== Year ========================== if currYDCF == 'year' then if nav == nil then local args = { ['min'] = minYear, ['skip-gaps'] = 'yes' } if parentArg and currentTitle.namespace ~= 14 then args['testcase'] = parentArg end nav = frame:expandTemplate{ title = 'Category series navigation', args = args } end if parent == 'century' then if isNilOrEmpty(sortkey) then sortkey = currYear end --default to currYear categories[iparent] = '' elseif parent == 'biology' then if isNilOrEmpty(sortkey) then sortkey = '' --default to none else sortkey = '|'..sortkey end if tonumber(currYear) < 1865 then categories[iparent] = '' --biology cat structure doesn't exist pre-1865, as of 10/2018 else categories[iparent] = '' --if/when all biology cats exists, merge this elseif with 'paleontology' end elseif parent == 'paleontology' then if isNilOrEmpty(sortkey) then sortkey = '' --default to none else sortkey = '|'..sortkey end categories[iparent] = '' elseif parent == 'environment' then if isNilOrEmpty(sortkey) then sortkey = '' --default to none else sortkey = '|'..sortkey end categories[iparent] = '' elseif mw.ustring.match(parent, '^%u[%l ]+') then --e.g. Animals/Insects/Fossil taxa if isNilOrEmpty(sortkey) then sortkey = '' --default to none else sortkey = '|'..sortkey end categories[iparent] = '' else trackingCats[2] = '' --invalid year-parent end --======================== Century ========================= elseif currYDCF == 'century' then if nav == nil then local args = {} if parentArg and currentTitle.namespace ~= 14 then args['testcase'] = parentArg end nav = frame:expandTemplate{ title = 'Container category' } .. frame:expandTemplate{ title = 'Category series navigation', args = args } end if parent == 'formal' then if isNilOrEmpty(sortkey) then sortkey = addOrd(currCent) end --default to currCent categories[iparent] = '' elseif parent == 'biology' then if isNilOrEmpty(sortkey) then sortkey = '' --default to none else sortkey = '|'..sortkey end if tonumber(currCent) < 19 then categories[iparent] = '' --biology cat structure doesn't exist pre-1865, as of 10/2018 else categories[iparent] = '' --if/when all biology cats exists, merge this elseif with 'paleontology' end elseif parent == 'paleontology' then if isNilOrEmpty(sortkey) then sortkey = '' --default to none else sortkey = '|'..sortkey end categories[iparent] = '' elseif parent == 'environment' then if isNilOrEmpty(sortkey) then sortkey = '' --default to none else sortkey = '|'..sortkey end categories[iparent] = '' elseif mw.ustring.match(parent, '^%u[%l ]+') then --e.g. Animals/Insects/Fossil taxa if isNilOrEmpty(sortkey) then sortkey = '' --default to none else sortkey = '|'..sortkey end categories[iparent] = '' else trackingCats[2] = '' --invalid century-parent end --======================== Formal ========================== elseif currYDCF == 'formal' then local formalParentsDefaultSortkey_Space = { ['Animals'] = true, ['Insects'] = true, ['Molluscs'] = true, ['Fungi'] = true, }					local formalParentsDefaultSortkey_None = { ['Species'] = true, ['Taxa'] = true, ['Fossil taxa'] = true, }					if nav == nil then nav = frame:expandTemplate{ title = 'Container category' } end if parent == 'Group' then if isNilOrEmpty(sortkey) then sortkey = ' Year' end --default to " Year" categories[iparent] = '' elseif parent == 'paleontology' then if isNilOrEmpty(sortkey) then sortkey = ' ' end --default to " "; special parent categories[iparent] = '' elseif parent then --allow freeform formal-parents, as long as they exist if mw.title.new( parent, 'Category' ).exists then if sortkey then categories[iparent] = '' else categories[iparent] = '' end else trackingCats[2] = '' --invalid freeform formal-parent end elseif formalParentsDefaultSortkey_Space[parent] then if isNilOrEmpty(sortkey) then sortkey = ' ' end --default to " "; normal parent categories[iparent] = '' elseif formalParentsDefaultSortkey_None[parent] then if isNilOrEmpty(sortkey) then sortkey = '' --default to none; normal parent else sortkey = '|'..sortkey end categories[iparent] = '' else trackingCats[2] = '' --invalid formal-parent end --========================= Error ========================== else trackingCats[2] = '' --unknown configuration end iparent = iparent + 1 parenti = 'parent'..iparent sortkeyi = 'sortkey'..iparent end --while conf[currGroup][currYDCF][parenti] do		end --if bConfError == false then --check for non-existent cats for _, category in pairs(categories) do			local cat = mw.ustring.match(category, '%[%[Category:([%w%s]+)') if mw.title.new(cat, 14).exists == false then trackingCats[1] = '' break end end end --if currCat then --build header & rem surrounding whitespace local br = ' '	if nav then header = nav end if portal then header = header..'\n'..portal end if commons then header = header..'\n'..commons end if wikispecies then header = header..'\n'..wikispecies end if description and description ~= '' then header = header..description elseif portal or commons or wikispecies then header = mw.ustring.gsub(header, '', '') end if toc then header = header..br..toc end header = mw.text.trim(header) header = mw.ustring.gsub(header, '^ ', '') header = mw.ustring.gsub(header, ' $', '') --append header to outString if outString then outString = outString..header else outString = header end --append cats to outString if currentTitle.namespace == 14 then --category namespace if table.maxn(categories) > 0 then outString = outString..table.concat(categories) end outString = outString..table.concat(trackingCats) else if table.maxn(categories) > 0 then --might be 0 if there's an error before setting cats outString = outString..br..mw.ustring.gsub(table.concat(categories, br), '%[%[', '[[:')		end		outString = outString..br..mw.ustring.gsub(table.concat(trackingCats, br), '%[%[', '[[:')		outString = mw.ustring.gsub(outString, br..br, br) --produced by empty ('') first/consecutive tracking cat/s		outString = mw.ustring.gsub(outString, br..br, br) --jic (use while loop if #trackingCats >= 3 or 4)	end	return outString end

return p