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/config' ) --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 {{Portal|...}}

local commons = nil --for {{Commons|...}}

local wikispecies = nil --for {{Wikispecies|...}}

local description = nil

local toc = nil

local categories = {}

local trackingCats = {

[1] = '', --placeholder for Category:Described in year unknown category

[2] = '', --placeholder for Category:Described in year error

[3] = '', --placeholder for Category:Described in year with manual category

}

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] = 'P' --missing a category parameter outside category namespace

end

end

end

--find commons & wikispecies link(s); produce {{Commons and category}} and/or {{Wikispecies}} 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] = 'D' --invalid decade-parent (deprecated)

elseif currCent then

currYDCF = 'century'

elseif currFrml then

currYDCF = 'formal'

else

bConfError = true

trackingCats[2] = 'N' --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] = '1' --group (Bacteria/Plants/etc.) key missing from conf

elseif conf[currGroup][currYDCF] == nil then

bConfError = true

trackingCats[2] = '2' --year/century/formal key missing

else

if conf[currGroup][currYDCF].description == nil then

bConfError = true

trackingCats[2] = '3' --description key missing

end

if conf[currGroup][currYDCF].parent1 == nil then

bConfError = true

trackingCats[2] = '4' --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] = ''..sortkey..''

elseif parent == 'biology' then

if isNilOrEmpty(sortkey) then sortkey = '' --default to none

else sortkey = '|'..sortkey end

if tonumber(currYear) < 1865 then

categories[iparent] = 'Category:'..currYear..' in science'..sortkey..'' --biology cat structure doesn't exist pre-1865, as of 10/2018

else

categories[iparent] = 'Category:'..currYear..' in biology'..sortkey..'' --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] = 'Category:'..currYear..' in '..parent..sortkey..''

elseif parent == 'environment' then

if isNilOrEmpty(sortkey) then sortkey = '' --default to none

else sortkey = '|'..sortkey end

categories[iparent] = 'Category:'..currYear..' in the environment'..sortkey..''

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] = 'Category:'..parent..' described in '..currYear..sortkey..''

else

trackingCats[2] = 'Y' --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] = ''..sortkey..''

elseif parent == 'biology' then

if isNilOrEmpty(sortkey) then sortkey = '' --default to none

else sortkey = '|'..sortkey end

if tonumber(currCent) < 19 then

categories[iparent] = 'Category:'..addOrd(currCent)..' century in science'..sortkey..'' --biology cat structure doesn't exist pre-1865, as of 10/2018

else

categories[iparent] = 'Category:'..addOrd(currCent)..' century in biology'..sortkey..'' --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] = 'Category:'..addOrd(currCent)..' century in '..parent..sortkey..''

elseif parent == 'environment' then

if isNilOrEmpty(sortkey) then sortkey = '' --default to none

else sortkey = '|'..sortkey end

categories[iparent] = 'Category:'..addOrd(currCent)..' century in the environment'..sortkey..''

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] = 'Category:'..parent..' described in the '..addOrd(currCent)..' century'..sortkey..''

else

trackingCats[2] = 'C' --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] = ''..sortkey..''

elseif parent == 'paleontology' then

if isNilOrEmpty(sortkey) then sortkey = ' ' end --default to " "; special parent

categories[iparent] = ''..sortkey..''

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] = ''..sortkey..''

else

categories[iparent] = 'Category:'..parent..''

end

else

trackingCats[2] = 'G' --invalid freeform formal-parent

end

elseif formalParentsDefaultSortkey_Space[parent] then

if isNilOrEmpty(sortkey) then sortkey = ' ' end --default to " "; normal parent

categories[iparent] = ''..sortkey..''

elseif formalParentsDefaultSortkey_None[parent] then

if isNilOrEmpty(sortkey) then sortkey = '' --default to none; normal parent

else sortkey = '|'..sortkey end

categories[iparent] = 'Category:'..parent..' by year of formal description'..sortkey..''

else

trackingCats[2] = 'F' --invalid formal-parent

end

--========================= Error ==========================

else

trackingCats[2] = 'U' --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] = 'Category:Described in year unknown category'

break

end

end

--check for manual cats

if currentTitle.namespace == 14 then --category namespace

local currContent = mw.title.makeTitle( 'Category', currCat or '' ):getContent()

local mancat = mw.ustring.match(currContent or '', '%[%[%s*Category')

if mancat then trackingCats[3] = 'Category:Described in year with manual category' end

end

end --if currCat then

--build header

local br = '
'

local n = '\n'

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, br, '')

end

if toc then header = header..br..toc end

--rem surrounding whitespace

header = mw.text.trim(header)

header = mw.ustring.gsub(header, '^'..br, '')

header = mw.ustring.gsub(header, br..'$', '')

--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), '%[%[', '[[:')

--ws cleanup

while string.match(outString, br..br) do --rem dup brs produced by empty ('') first/consecutive tracking cat/s

outString = string.gsub(outString, br..br, br)

end

end

return outString

end

return p