UsageEdit

  • General syntax: {{#invoke:Sandbox/Tom.Reding|function_name|parameter}}

instanceOfsEdit

  • {{#invoke:Sandbox/Tom.Reding/Tools|instanceOfs|Coelacanth}}Coelacanth no QID found

istaxonEdit

  • {{#invoke:Sandbox/Tom.Reding/Tools|istaxon|Coelacanth}}Coelacanth Q# not on Wikidata OR no sitelink
  • {{#invoke:Sandbox/Tom.Reding/Tools|istaxon|Douglas Adams}}Douglas Adams Q# not on Wikidata OR no sitelink
  • {{#invoke:Sandbox/Tom.Reding/Tools|istaxon|Universe}}Universe Q# not on Wikidata OR no sitelink



local p = {}

function p.fmttimestamp(frame)
	local ts = frame.args[1] --20190201223349
	local y  = string.sub(ts,1,4)
	local mo = string.sub(ts,5,6)
	local d  = string.sub(ts,7,8)
	local h  = string.sub(ts,9,10)
	local mi = string.sub(ts,11,12)
	local s  = string.sub(ts,13,14)
	return y..'-'..mo..'-'..d..'T'..h..':'..mi..':'..s --ISO 8601 format without time zone designator
end

function p.instanceOfs(frame)
-- returns the 'instance of'(s) (P31) of the WP page passed
-- good for finding exceptions in [[Category:Taxonbars on possible non-taxon pages]]
	local tab = '	'
	local resolveEntity = require( 'Module:ResolveEntityId' )
	local title = frame.args[1]
	local qid = resolveEntity._id(title)
	local item = mw.wikibase.getEntity(qid)
	if item then
		local tInstanceOfs = {}
		for i, instanceOfState in pairs ( item:getBestStatements('P31') ) do --instance of
			local instanceOf = instanceOfState.mainsnak.datavalue.value.id --'Q...'
--			local instanceOfLabel = mw.wikibase.getLabel(instanceOf) or 'nil' --plain English
			tInstanceOfs[#tInstanceOfs + 1] = '{{Q|'..instanceOf..'}}'
		end
		if not tInstanceOfs[1] then tInstanceOfs = { 'none found' } end
		return '[['..title..']]'..tab..'[[d:'..qid..'|'..qid..']]'..tab..'instance of: '..table.concat(tInstanceOfs, ', ')..'<br/>'
	else
		return '[['..title..']]'..tab..'no QID found<br/>'
	end
end

function p.istaxon(frame)
-- climbs the taxon tree until a description is found
-- prefix 'c' = child, 'p' = parent
-- output:	[[cSitelink or cQID]]	[[cQID]]		<cI/O>	(cI/O lbl)	<cTaxonQID>	<cTaxon QID label>	[[pQID|pDescription]]:				<pDescription's animal type>
--     ex:	[[Orthalicus nobilis]]	[[d:Q49525664]]	Q16521	(taxon)		Q7432		species				[[d:Q7104378|genus of molluscs]]:	mollusc
	local resolveEntity = require( 'Module:ResolveEntityId' )
	local title = frame.args[1]
	local qid = resolveEntity._id(title)
	local item = mw.wikibase.getEntity(qid)
	local acceptableInstanceOf = {
		['Q16521'] = 'taxon',
		['Q310890'] = 'monotypic taxon',
		['Q2568288'] = 'ichnotaxon',
		['Q23038290'] = 'fossil taxon',
		['Q47487597'] = 'monotypic fossil taxon',
	}
	local allowNonTaxons = false --if false (default), display '(not a taxon)';
	local otherInstanceOf = {    --if true, allow clades, etc., as named in otherInstanceOf{}
		['Q713623'] = 'clade',
	--	['Q4886'] = 'cultivar', --kind of a mess
	}
	local tab = '	'
	if item then
		
		--find child's rank
		local childRank, childRankLabel, species = nil
		local childRankState = item:getBestStatements('P105')[1] --taxon rank
		if childRankState then
			if childRankState.mainsnak.datavalue then
				childRank = childRankState.mainsnak.datavalue.value.id
				childRankLabel = mw.wikibase.getLabel(childRank)
				local speciesRanks = {
					['Q7432'] = 'species',
					['Q68947'] = 'subspecies',
					['Q4886'] = 'cultivar',
					['Q279749'] = 'form',
					['Q630771'] = 'subvariety',
					['Q767728'] = 'variety',
				--  these should all be plural; for reference only:
				--	['Q3025161'] = 'series',
				--	['Q3181348'] = 'section',
				}
				if speciesRanks[childRank] then
					species = 'species' --used later as a boolean
				end
			else
				childRank = 'missing taxon rank value'
				childRankLabel = '-'
			end
		end
		
		--find parent's description; if nil, search grandparent, etc., etc.
		local ancestorItem = item --initialize to child
		local bothDescriptionsFmtd = tab..tab --parent's raw descrip + child's new suggested descrip
		local parentDescription = nil
		local newChildDescription = nil
		local oldChildDescription = item:getDescription() --different output if nil vs present
		if oldChildDescription == nil then
			while bothDescriptionsFmtd == tab..tab do
				local parentState = ancestorItem:getBestStatements('P171')[1] --parent taxon
				if parentState then
					local parentId = parentState.mainsnak.datavalue.value.id
					if parentId then
						parentDescription = mw.wikibase.getDescription(parentId)
						if parentDescription then
							local trim = mw.ustring.gsub(parentDescription, '^[%w%s%(%)%-,]-%s+of%s+', '') --trim to first ' of ' to remove taxon rank/monotypic
							if species then
								--remove 's' semi-liberally
								trim = mw.ustring.gsub(trim, '^([%w]+[^s])s([, ])', '%1%2')   --birdS that ...; plantS, guava
								trim = mw.ustring.gsub(trim, '^([a-z]+[^s])s$', '%1')         --birdS
								trim = mw.ustring.gsub(trim, '([^a-zA-Z][a-z]+[^s])s$', '%1') --song and dance birdS
								trim = mw.ustring.gsub(trim, '^([%w%-]+ [a-z]+[^s])s([, ])', '%1%2') --song birdS in ...
								trim = mw.ustring.gsub(trim, 's %(fossil', ' (fossil') --birdS (fossil)
							--	trim = mw.ustring.gsub(trim, 's %(',  ' (')    --turn off if too liberal
								trim = mw.ustring.gsub(trim, 's of ', ' of ')  --triasic birdS of the family...
								trim = mw.ustring.gsub(trim, 's in the ', ' in the ')  --triasic birdS in the family...
								trim = mw.ustring.gsub(trim, '^bacteria', 'bacterium')
								trim = mw.ustring.gsub(trim, '[^%w]bacteria', 'bacterium')
								--fix special cases
								trim = mw.ustring.gsub(trim, 'algae', 'alga')
								trim = mw.ustring.gsub(trim, 'cactu(%f[\0, ])', 'cactus%1') --only way to combine rules with Lua's shitty regex; %f doesn't recognize [^s]...
								trim = mw.ustring.gsub(trim, 'cactuse(%f[\0, ])', 'cactus%1')
								trim = mw.ustring.gsub(trim, 'carnivorou(%f[\0, ])', 'carnivorous%1')
								trim = mw.ustring.gsub(trim, 'countrie(%f[\0, ])', 'countries%1')
								trim = mw.ustring.gsub(trim, 'citru(%f[\0, ])', 'citrus%1')
								trim = mw.ustring.gsub(trim, 'crocu(%f[\0, ])', 'crocus%1')
								trim = mw.ustring.gsub(trim, 'deciduou(%f[\0, ])', 'deciduous%1')
								trim = mw.ustring.gsub(trim, '[eE]delweis(%f[\0, ])', 'edelweiss%1')
								trim = mw.ustring.gsub(trim, 'fishe(%f[\0, ])', 'fish%1')
								trim = mw.ustring.gsub(trim, 'flightles(%f[\0, ])', 'flightless%1')
								trim = mw.ustring.gsub(trim, 'fung[iu](%f[\0, ])', 'fungus%1')
								trim = mw.ustring.gsub(trim, '[gG]enu(%f[\0, ])', 'genus%1')
								trim = mw.ustring.gsub(trim, '[gG]ras(%f[\0, ])', 'grass%1')
								trim = mw.ustring.gsub(trim, '[gG]rasse(%f[\0, ])', 'grass%1')
								trim = mw.ustring.gsub(trim, 'herbaceou(%f[\0, ])', 'herbaceous%1')
								trim = mw.ustring.gsub(trim, 'herbivorou(%f[\0, ])', 'herbivorous%1')
								trim = mw.ustring.gsub(trim, '[iI]ri(%f[\0, ])', 'iris%1')
								trim = mw.ustring.gsub(trim, 'leeche(%f[\0, ])', 'leech%1')
								trim = mw.ustring.gsub(trim, 'loache(%f[\0, ])', 'loach%1')
								trim = mw.ustring.gsub(trim, 'mos(%f[\0, ])', 'moss%1')
								trim = mw.ustring.gsub(trim, 'mosse(%f[\0, ])', 'moss%1')
								trim = mw.ustring.gsub(trim, '[sS]pecie(%f[\0, ])', 'species%1')
								trim = mw.ustring.gsub(trim, 'venomou(%f[\0, ])', 'venomous%1')
								trim = mw.ustring.gsub(trim, 'viruse(%f[\0, ])', 'virus%1')
							end
							trim = mw.ustring.gsub(trim, ' %(fossil%)', '') --" (fossil)" restored later IIF instance of = fossil taxon type
							if trim == parentDescription then trim = '' end --nothing changed, so no need to duplicate it
							newChildDescription = trim --used 'trim' just b/c it's shorter
							bothDescriptionsFmtd = tab..'[[d:'..parentId..'|'..parentDescription..']]:'..tab..newChildDescription
						else
							ancestorItem = mw.wikibase.getEntity(parentId)
						end
					else
						bothDescriptionFmtds = tab..'parent missing ID??'..tab --probably not a valid falure mode, but jic...
					end
				else
					bothDescriptionsFmtd = tab..'no parent taxon/P171'..tab
				end
			end --while
		end --if oldChildDescription == nil
		
		--test child for instance of: taxon
		local j = 0
		for i, instanceOfState in pairs ( item:getBestStatements('P31') ) do --child's instance of
			local instanceOf = instanceOfState.mainsnak.datavalue.value.id
			if acceptableInstanceOf[instanceOf] or 
			  (acceptableInstanceOf[instanceOf] == nil and otherInstanceOf[instanceOf] and allowNonTaxons == true) then
				
				--extinct/fossil handling in description
				local instanceOfLabel = mw.wikibase.getLabel(instanceOf)
				if oldChildDescription == nil then
					--only append ' (fossil)', when needed, to items missing descrip;
					--retroactively making existing descriptions match their 'instance of taxon rank' is a different/much larger job
					if instanceOfLabel == 'fossil taxon' or 
					   instanceOfLabel == 'monotypic fossil taxon' or
					   instanceOfLabel == 'ichnotaxon' then
						local childSaysFossil = string.match(newChildDescription, 'extinct') or
												string.match(newChildDescription, 'dinosaur') or
												string.match(newChildDescription, 'fossil')
						if childSaysFossil == nil then
							newChildDescription = newChildDescription..' (fossil)' --housekeeping
							bothDescriptionsFmtd = bothDescriptionsFmtd..' (fossil)' --goes to output
						end
					end
				else
					bothDescriptionsFmtd = tab..'-'..tab..'child has description: '..oldChildDescription
				end
				
				--output for successful items
				local title_qid = '[['..title..']]'..tab..'[[d:'..qid..'|'..qid..']]'
				if childRank == nil then childRank = 'UNRANKED' end
				if childRankLabel == nil then 
					if childRank == 'UNRANKED' then childRankLabel = 'UNRANKED' 
					else childRankLabel = 'MISSING LABEL' end
				end
				local cRank_cLabel_pDescrip = childRank..tab..childRankLabel..bothDescriptionsFmtd
				if instanceOfLabel == 'taxon' then
					return title_qid..tab..instanceOf..tab..'('..instanceOfLabel..')'..tab..cRank_cLabel_pDescrip
					
				elseif instanceOfLabel == 'monotypic taxon' then --force 'Q16521' output (taxon)
					return title_qid..tab..'Q16521'..tab..'('..instanceOfLabel..')'..tab..cRank_cLabel_pDescrip
					
				elseif instanceOfLabel == 'monotypic fossil taxon' then --force 'Q23038290' output (fossil taxon)
					return title_qid..tab..'Q23038290'..tab..'('..instanceOfLabel..')'..tab..cRank_cLabel_pDescrip
					
				else --ichno or polytypic fossil
					return title_qid..tab..instanceOf..tab..'('..instanceOfLabel..')'..tab..cRank_cLabel_pDescrip
				end
			end
			j = i
		end --for child's instance ofs
		
		--output for failed items
		if j > 0 then
			return '[['..title..']]'..tab..'[[d:'..qid..'|'..qid..']]'..tab..'instanceOf:'..tab..'(not a taxon)'
		else
			return '[['..title..']]'..tab..'[[d:'..qid..'|'..qid..']]'..tab..'instanceOf:'..tab..'(not a taxon? no instanceOf)'
		end
		
	else
		--output for failed items
		if qid then
			return '[['..title..']]'..tab..'[[d:'..qid..'|'..qid..']]'..tab..'item'..tab..'(not a taxon? item lookup failed)'
		else
			return '[['..title.. ']]'..tab..''..'Q#'..tab..'not on Wikidata OR no sitelink'
		end
	end
end

function p.getTaxonRank(frame)
	local resolveEntity = require( 'Module:ResolveEntityId' )
	local title = frame.args[1]
	local qid = resolveEntity._id(title)
	local item = mw.wikibase.getEntity(qid)
	local taxonRanks = {
		['Q7432'] = 'species',
		['Q34740'] = 'genus',
		['Q35409'] = 'family',
		['Q36602'] = 'order',
		['Q37517'] = 'class',
		['Q38348'] = 'phylum',
		['Q2007442'] = 'infraclass',
		['Q2136103'] = 'superfamily',
		['Q227936'] = 'tribe',
		['Q2455704'] = 'subfamily',
		['Q2889003'] = 'infraorder',
		['Q3238261'] = 'subgenus',
		['Q5867051'] = 'subclass',
		['Q5867959'] = 'suborder',
		['Q5868144'] = 'superorder',
		['Q68947'] = 'subspecies',
	}
	
	if qid then
		if item then
			local tab = '&#09;'
			local rankState = item:getBestStatements('P105')[1] --taxon rank
			if rankState then
				local rank = rankState.mainsnak.datavalue.value.id
				if rank then
					if taxonRanks[rank] then return taxonRanks[rank]
					else return rank end
				else return 'No rank found' end
			else
				return 'rankState not found'
			end
		else
			return 'Item not found'
		end
	else
		return 'QID not found'
	end
end

return p