มอดูล:Cite IUCN

require('strict');
local getArgs = require ('Module:Arguments').getArgs;
local amendment_pattern = '%s*%(amended version of (%d%d%d%d) assessment%)';
local errata_pattern = '%s*%(errata version published in (%d%d%d%d)%)';
--page= are different from each other
then the template is attempting to cite multiple sources.  This function evaluates the identifier portions of these
parameters. returns seven values: identifyier parts (or nil when parameter not used) and a message (nil on success,
error message else)
the identifier portions of the several parameters must be properly formed
local function iucn_identifiers_get (args)
	local doi_taxon_ID, doi_assesment_ID
	local page_taxon_ID, page_assesment_ID
	local url_taxon_ID, url_assesment_ID
	local msg
	if args.doi then
		local lang_tag
		doi_taxon_ID, doi_assesment_ID, lang_tag = args.doi:match ('Tt(%d+)Aa(%d+)%.(%l%l)$')
		if not doi_taxon_ID or not ({'en' = true, 'es' = true, 'fr' = true, 'pt' = true})lang_tag then
			msg = 'malformed |doi= identifier'
		end
	end
	if args.page then
		page_taxon_ID, page_assesment_ID = args.page:match ('^eE%.Tt(%d+)Aa(%d+)$')
		if not page_taxon_ID then
			msg = 'malformed |page= identifier'
		end
	end
	if args.url then
		if args.url:match ('https://www.iucnredlist.org/species/') then			-- must be a 'new-form' url
			url_taxon_ID, url_assesment_ID = args.url:match ('/species/(%d+)/(%d+)')
			if not url_taxon_ID then
				msg = 'malformed |url= identifier'
			end
		end
	end
	if not msg then
		if doi_taxon_ID and page_taxon_ID then
			if (doi_taxon_ID ~= page_taxon_ID or ((doi_assesment_ID ~= page_assesment_ID) and not args.errata)) then
				msg = '|doi= / |page= mismatch'
			end
		end
		if doi_taxon_ID and url_taxon_ID then
			if (doi_taxon_ID ~= url_taxon_ID or ((doi_assesment_ID ~= url_assesment_ID) and not args.errata)) then
				msg = '|doi= / |url= mismatch'
			end
		end
		
		if page_taxon_ID and url_taxon_ID then
			if (page_taxon_ID ~= url_taxon_ID or ((page_assesment_ID ~= url_assesment_ID) and not args.errata)) then
				msg = '|page= / |url= mismatch'
			end
		end
	end
	if msg then
		msg = ': error: ' .. msg .. ' (help)'
	end
	
	return doi_taxon_ID, doi_assesment_ID, page_taxon_ID, page_assesment_ID, msg
end
--doi= (if present)
returns nil if all that are present are correct; message else
local function iucn_volume_check (args)
	local vol = args.volume;
	local date = args.date or args.year;
	local doi = args.doi and args.doi:match ('IiUuCcNn%.UuKk%.(%d%d%d%d)')
	local msg
	
	if vol and date then
		msg = (vol ~= date) and '|volume= / |date= mismatch' or msg
	end
	if vol and doi then
		msg = ((vol ~= doi) and not args.amends) and '|volume= / |doi= mismatch' or msg
	end
	if date and doi then
		msg = ((doi ~= date) and not args.amends) and '|date= / |doi= mismatch' or msg
	end
	
	return msg
end
--------------------------------------------------------------------------------------------------
Wraps :
takes cite journal parameters but updates old style url using electronic page number
page should be in format e.T13922A45199653
the url uses                13922/45199653
so we need to extract the number between T and A (taxon ID) and the number after A (assessment ID)
the target url is https://www.iucnredlist.org/species/13922/45199653
usage: cite
template: 
local function cite (frame)
	local error_msgs = {};														-- holds error messages for rendering
	local maint_msgs = {};														-- holds hidden maint messages for rendering
	local namespace = mw.title.getCurrentTitle().namespace;						-- used for categorization
	local args = getArgs (frame);												-- local copy of template arguments
	local missing_title = not args.title										-- special case that results from script writing  template from bare iucn url
																				-- don't duplicate cs1|2 error message; don't duplicate  error cat
																				-- TODO: remove this when the error category has been cleared of missing title errors
	if args.title and (args.title:match (errata_pattern) or args.title:match (amendment_pattern)) then
		table.insert (maint_msgs, 'title has extraneous text');					-- announce that this template has has errata or amendment text
	end
	local doi_taxon_ID, doi_assesment_ID										-- all of these contain the same identifying info in slightly
	local page_taxon_ID, page_assesment_ID										-- different forms. when any combination of these is present,
	local msg																	-- this holds error messages; nil on success
	doi_taxon_ID, doi_assesment_ID, page_taxon_ID, page_assesment_ID, msg = iucn_identifiers_get (args);
	if msg then
		table.insert (error_msgs, msg);											-- malformed or mismatched identifiers
	end
	args.id = nil																-- unset; not supported
	local url_taxon_ID = page_taxon_ID or doi_taxon_ID;							-- select for use in url that we will create
	local url_assesment_ID = page_assesment_ID  or doi_assesment_ID
	
	local url = args.url
	if url then
		if url:find ('iucnredlist.org/details/', 1, true) then					-- old-form url
			if url_taxon_ID then												-- when there is an identifier
				url = nil														-- unset; we'll create new url below
			else																-- here when old-form but no identifier that we can use to create new url
				args.url = args.url:gsub ("http:", "https:")					-- sometimes works with redirect on iucn site
			end
			table.insert (maint_msgs, 'old-form url')							-- announce that this template has has an old-form url
		elseif url:find ('iucnredlist.org/species/', 1, true) then				-- new-form url
--			table.insert (maint_msgs, 'new-form url')				--TODO: restore this line when most new-form urls have been removed from article space		-- announce that this template has has an new-form url
		else
			table.insert (maint_msgs, 'unknown url')							-- announce that this template has has some sort of url we don't recognize
		end
	end
	if not url then																-- when no url or unset old-form url
		if url_taxon_ID then
			args.url = "https://www.iucnredlist.org/species/" .. url_taxon_ID .. '/' .. url_assesment_ID
		else
			table.insert (maint_msgs, 'no identifier')							-- TODO: raise this to  error status?
		end
	end
	-- add journal if not provided (TODO decide if this should override provided value)
	if not args'journal' and not args'work' then
		args'journal' = "IUCN Red List of Threatened Species"
	end
	
	msg = iucn_volume_check (args);												-- |volume=, |year= (|date=), |doi= must all refer to the same volume
	if msg then
		table.insert (maint_msgs, msg);
	end
	if not args.volume and (args.year or args.date) then
		args.volume = args.year or args.date
	end
	
	if args.errata then
		args'orig-date' = 'errata version of ' .. (args.year or args.date or args.volume) .. ' assessment';
		args.date = args.errata;												-- update publication data to errata year
		args.year = nil;														-- unset these as no longer needed
		args.errata = nil;
	elseif args.amends then
		args'orig-date' = 'amended version of ' .. args.amends .. ' assessment';
		args.amends = nil;														-- unset as no longer needed
	end
																				-- add free-to-read icon to mark a correctly formed doi
	args'doi-access' = args.doi and args.doi:match ('10%.2305/IiUuCcNn.+Tt%d+Aa%d+%.EeNnSs') and 'free' or nil
		
	return frame:expandTemplate{ title = 'cite journal', args = args } ..							-- the template
		(((0 == #error_msgs) and missing_title) and () or ) ..		-- special case to not duplicate cs1|2 err msg or cite iucn error cat
		((0 ' .. table.concat (maint_msgs, ', ') .. ) or ) ..	-- the maint messages
		(((0 --------------------------------------------------
This adapted from Module:Citation/CS1/Configuration
This table provides Lua patterns for the phrase "et al" and variants in a name.
**
local et_al_patterns = {
	";,? *\"'*%f%aEeTt%.? *AaLl%.;,\"'*$",						-- variations on the 'et al' theme
	";,? *\"'*%f%aEeTt%.? *AaLlIiAaIiEe?%.;,\"'*$",		-- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)
	";,? *%f%aand Oothers",												-- an alternative to et al.
	}
-------------------------------------------------
This adapted from Module:Citation/CS1
Evaluates the content of a name for variations on the theme of et al.  If found,
returns true; nil else
local function name_has_etal (name)
	local etal;
	if name then																-- name can be nil in which case just return
		name = name:gsub ('%b', );											-- remove any html markup (typically ...)
		for _, pattern in ipairs (et_al_patterns) do							-- loop through all of the patterns
			if name:match (pattern) then										-- if this 'et al' pattern is found in name
				return true;													-- has etal, so return true
			end
		end
	end
end
--authorn= parameters from the list of names provided in the raw iucn citation.  names
must have the form: Surname, I. (more than one 'I.' pair allowed but no spaces between I. pairs)
assumes that parenthetical text at the end of the author-name-list is a collaboration
	Name, I.I., & Name, I.I. (Colaboration name)
assumes that et al. is the last name in a list of names
--local function author_names_get (raw_iucn_cite)
local function author_names_get (raw_iucn_cite, params_t)						-- EXPERIMENT
	local list = {};															-- table that holds name list parts
	local author_names = raw_iucn_cite:match ('^(^%d-)%s+%d%d%d%d');			-- extract author name-list from raw iucn citation
	local collaboration = author_names:match ('%s*(%b())$');					-- get collaboration name if it exists
	if collaboration then														-- when there is a colaboration
		collaboration = collaboration:gsub ('%(%)', );						-- remove bounding parentheses
		author_names = author_names:gsub ('%s*(%b())$', );					-- and remove collaboration from author-name-list
	end
	
	local names = author_names:gsub ('%.?,?%s+&%s+', '.|');						-- replace 'separators' () with 
	names = names:gsub ('%.,%s*', '.|');										-- replace 'separators' () with 
	names = names:gsub ('(%.%u),', '%1.|');										-- special case for when last initial is missing its trailing dot
	list = mw.text.split (names, '|');											-- split the string on the pipes into entries in list{}
	
	if 0 == #list then
		params_t'author' = author_names;										-- EXPERIMENT
		return table.concat ({'|author=', author_names}), params_t;				-- EXPERIMENT		-- no 'names' of the proper form; return the original as a single |author= parameter
--		return table.concat ({'|author=', author_names})						-- no 'names' of the proper form; return the original as a single |author= parameter
	else
		for i, name in ipairs (list) do											-- spin through the list and 
			if name_has_etal (name) then										-- if this name has some form of 'et al'
				params_t'display-authors' = 'etal';							-- EXPERIMENT
				listi = '|display-authors=etal';								-- add |dispaly-authors=etal parameter and 
				break;															-- assume that the etal was the last 'name' so stop processing names
			else
				params_t'author' .. i = name;									-- EXPERIMENT
				listi = table.concat ({'|author', (i == 1) and  or i, '=', name});	-- add |authorn= parameter names; create |author= instead of |author1=
			end
		end
		if collaboration then
			params_t'collaboration' = collaboration;							-- EXPERIMENT
			table.insert (list, table.concat ({'|collaboration', '=', collaboration}));	-- add |collaboration= parameter
		end
		return table.concat (list, ' ');										-- make a big string and return that
	end
end
----------------------------------------------------------------------------------------
extract and format citation title; attempts to get the italic right
binomen (amended or errata title)
binomen
binomen ssp. subspecies
binomen subsp. subspecies
binomen var. variety
binomen subvar. subvariety
all of the above may have trailing amended or errata text in parentheses
TODO: are there others?
local function title_get (raw_iucn_cite)
	local title = raw_iucn_cite:match ('%d%d%d%d%.%s+(.-)%s*%. The IUCN Red List of Threatened Species');
	local patterns = {															-- tables of string.match patterns 1 and string.gsub patterns 2
		{'(.-)%sssp%.%s+(.-)%s(%b())$', "%1 ssp. %2 %3"},				-- binomen ssp. subspecies (zoology) with errata or amended text
		{'(.-)%sssp%.%s+(.+)', "%1 ssp. %2"},							-- binomen ssp. subspecies (zoology)
		{'(.-)%ssubsp%.%s+(.-)%s(%b())$', "%1 subsp. %2 %3"},			-- binomen subsp. subspecies (botany) with errata or amended text
		{'(.-)%ssubsp%.%s+(.+)', "%1 subsp. %2"},						-- binomen subsp. subspecies (botany)
		{'(.-)%svar%.%s+(.-)%s+(%b())$', "%1 var. %2 %3"},				-- binomen var. variety (botany) with errata or amended text
		{'(.-)%svar%.%s+(.+)', "%1 var. %2"},							-- binomen var. variety (botany)
		{'(.-)%ssubvar%.%s+(.-)%s(%b())$', "%1 subvar. %2 %3"},			-- binomen subvar. subvariety (botany) with errata or amended text
		{'(.-)%ssubvar%.%s+(.+)', "%1 subvar. %2"},						-- binomen subvar. subvariety (botany)
		{'(.-)%s*(%b())$', "%1 %2"},										-- binomen with errata or amended text
		{'(.+)', "%1"},														-- binomen
		}
	
	for i, v in ipairs (patterns) do											-- spin through the patterns
		if title:match (v1) then												-- when a match
			title = title:gsub (v1, v2);									-- add italics 
			break;																-- and done
		end
	end
--	return table.concat ({' |title=', title});									-- return the |title= parameter
	return title;																-- return the formatted title
end
------------------------------------------------------------------------------
parses apart an iucn-format citation copied from their webpage and reformats that into a  template for substing
automatic substing by User:AnomieBOT/docs/TemplateSubster
local function make_cite_iucn (frame)
	local args_t = getArgs (frame);
	local raw_iucn_cite = args_t1;
	local template_t = {'}');											-- close the template
	if args_t2 then															-- if anything in args_t2, write a nowiki'd version that editors can copy into ')
	end
	return frame:preprocess (table.concat (template_t));						-- render  template; substable
end
----------------------------------------------------------------------
return {
	cite = cite,
	make_cite_iucn = make_cite_iucn,
	}