Module:GHS phrases/sandbox

--------------------------------------------------------------------------------

-- Module:GHS phrases

--

-- main: reads GHS parameters (arguments like "H301", "P401")

-- and returns for each (listtype='abbr'):

-- phraseID visible; formal phrase text as

-- setID = "H" or "P"

-- phraseID = e.g. "H201", "P231+P234"

-- phrase text read from array tables in Module:GHS phrases/data

--

-- Implements: Template:GHS phrases

-- Helppage: Template:GHS phrases

-- Error category: Category:GHS errors, Category:GHS warnings (mainspace pages only)

--

-- Also:

-- listAll(), numberOfPhrases(), listOmitRules(),

-- listtype, omit

--------------------------------------------------------------------------------

require('strict')

local r = {} -- "r" for return, so no confusion with setID P

local GHSdata = mw.loadData('Module:GHS phrases/data/sandbox')

local getArgs = require('Module:Arguments').getArgs

local tTools = require('Module:TableTools')

local yesno = require('Module:Yesno')

local tArgName = {} -- named parameters (setid, omit, listtype)

local tMessagesToShow = {} -- the tail: Preview, Categories

--------------------------------------------------------------------------------

-- wlHelpPage

--

-- Formats page as Label

-- by default, sLabel == sSection

--------------------------------------------------------------------------------

local function wlHelpPage(sSection, sLabel)

local sHelpPage = 'Template:GHS phrases'

if sLabel == nil then sLabel = sSection end

if (sLabel or ) == then

sLabel = ''

else

sLabel = '|' .. sLabel

end

if (sSection or ) == then

sSection = ''

else

sSection = '#' .. sSection

end

return '' .. sHelpPage .. sSection .. sLabel .. ''

end

--------------------------------------------------------------------------------

-- addErrorCategory

--

-- Formats as catsort

-- or '' when in other namespace.

-- sCatsort option using: H, P, _

--------------------------------------------------------------------------------

local function addErrorCategory(sCatsort)

local pagetype = require('Module:Pagetype').main

local wlErrCat = ''

if pagetype() == 'article' then -- mainspace only

if sCatsort == nil then sCatsort = tArgName['setID'] end

if sCatsort == '' then

wlErrCat = 'Category:GHS errors'

else

wlErrCat = '' .. sCatsort .. ''

end

else

return ''

end

table.insert(tMessagesToShow, wlErrCat)

return

end

--------------------------------------------------------------------------------

-- addWarningCategory

--

-- Formats as catsort

-- mainspace only, or '' when in other namespace.

-- sCatsort option using: H, P, U, ?, D, O

--------------------------------------------------------------------------------

local function addWarningCategory(sCatsort)

local pagetype = require('Module:Pagetype').main

if sCatsort == nil then sCatsort = tArgName['setID'] end

local wlWarnCat = ''

if pagetype() == 'article' then -- mainspace only

if sCatsort == '' then

wlWarnCat = 'Category:GHS warnings'

else

wlWarnCat = '' .. sCatsort .. ''

end

else

return

end

table.insert(tMessagesToShow, wlWarnCat)

return

end

--------------------------------------------------------------------------------

-- addPreviewMsg

--------------------------------------------------------------------------------

local function addPreviewMsg(sMsg)

local previewWarn = require('Module:If preview')._warning

table.insert(tMessagesToShow, previewWarn({sMsg}))

return

end

--------------------------------------------------------------------------------

-- showPreviewMsg

--

-- show table tMessagesToShow

-- preview-messages and errorcat

-- all namespaces

--------------------------------------------------------------------------------

local function showPreviewMsg()

if tTools.size(tMessagesToShow) > 0 then

return table.concat(tMessagesToShow, '')

else

return ''

end

end

--------------------------------------------------------------------------------

-- applyRemoveDuplicates

--

-- returns edited table, with double Codes removed

-- adds warning with codes.

-- base table tArgs is walked through by a iwalker that reads a singel code,

-- then a ikiller checks the upward part of the same table to delete all copies

-- ikiller starts at end of table, walks towards iwalker; then tArgs is compressed

-- iwalker steps 1 up in the freshly compressed table

-- Used: iArgs is sorted, and order stays same. compress does not change that.

--------------------------------------------------------------------------------

local function applyRemoveDuplicates(tArgs)

local iR, iK -- iR = reader, iK = killer

local hit = false

iR = 1

while iR < #tArgs do

iK = #tArgs -- will be counting downwards

while iK > iR do

if tArgs[iK] == tArgs[iR] then

hit = true

addPreviewMsg('Duplicate removed: ' .. tArgs[iR])

table.remove(tArgs, iK)

tTools.compressSparseArray(tArgs)

end

iK = iK - 1

end

tTools.compressSparseArray(tArgs)

iR = iR + 1

end

if hit then

addWarningCategory('D')

end

return tArgs

end

--------------------------------------------------------------------------------

-- applyOmitRules

--

-- returns edited table, with Omit phraseID's removed

-- Omit rule is per GHS_Rev9E_0.pdf (2021)

--------------------------------------------------------------------------------

local function applyOmitRules(tArgs)

local tRules = GHSdata['tOmitRules']

local hit = false

for keep, omit in pairs(tRules) do

if tTools.inArray(tArgs, omit) then

if tTools.inArray(tArgs, keep) then

hit = true

for i, k in pairs(tArgs) do

if k == omit then

table.remove(tArgs, i)

end

end

addPreviewMsg(wlHelpPage('Omit Rules') .. ': keep ' .. keep .. ', omit ' .. omit)

end

end

end

if hit then

tTools.compressSparseArray(tArgs)

addWarningCategory('O')

end

return tArgs

end

--------------------------------------------------------------------------------

-- label H-phrases or P-phrases

--------------------------------------------------------------------------------

local function PHlabel()

if tArgName['setID'] == 'GHS' then

return 'GHS phrases'

else

return tArgName['setID'] .. '-phrases'

end

end

--------------------------------------------------------------------------------

-- inMono

--

-- Use mono font-family (from: Template:Mono)

--------------------------------------------------------------------------------

local function inMono(s)

if s == nil then s = '' end

return '' .. s .. ''

end

--------------------------------------------------------------------------------

-- wlInlineTag

--

-- Returns [?] with wikilink to errormessage

--------------------------------------------------------------------------------

local function wlInlineTag(phraseID)

local sMsg

sMsg = '['

.. wlHelpPage(PHlabel(), '?')

.. ']'

return sMsg

end

--------------------------------------------------------------------------------

-- errorPhraseIDnotFound

--

-- Returns single value when error (not found in list):

-- plain value + inline warning [?] (linked) + error cat (mainsp) + preview warning

--------------------------------------------------------------------------------

local function errorPhraseIDnotFound(phraseID)

if phraseID == nil then phraseID = '' end

local inlineTag = wlInlineTag(phraseID)

local previewMsg = wlHelpPage(PHlabel()) .. ': \"' .. phraseID .. '\" not found'

addPreviewMsg(previewMsg)

addErrorCategory()

return phraseID .. inlineTag

end

--------------------------------------------------------------------------------

-- errorHPsetIDnotFound

--

-- setID H or P could not be found

--------------------------------------------------------------------------------

local function errorHPsetIDnotFound()

local sMsg

sMsg = wlHelpPage('', PHlabel())

.. ': "H" or "P" set id not found'

.. ' (please use form like "|H200" or "|P300+P301")'

addPreviewMsg(sMsg)

addErrorCategory('?')

return showPreviewMsg()

end

--------------------------------------------------------------------------------

-- errorHPsetIDmissing

--

-- parameter |setid= to be used

--------------------------------------------------------------------------------

local function errorHPsetIDmissing()

local sMsg

sMsg = wlHelpPage( '', PHlabel())

.. ': "H" or "P" set id not found,'

.. ' please use |setid=... (H or P)'

addPreviewMsg(sMsg)

return

end

--------------------------------------------------------------------------------

-- formatPhraseAbbr

--

-- format phraseID and text, for abbr-form (infobox list form)

--------------------------------------------------------------------------------

local function formatPhraseAbbr(phraseID, sPhrase)

return ''

.. phraseID

.. ''

end

--------------------------------------------------------------------------------

-- formatPhraseInline

--

-- format phraseID and text, for inline form (in sentence)

-- adds "quotes"

--------------------------------------------------------------------------------

local function formatPhraseInline(phraseID, sPhrase)

return inMono(phraseID) .. ': \"' .. sPhrase .. '\"'

end

--------------------------------------------------------------------------------

-- formatPhraseList

--

-- as inline, but no "quotes" added.

--------------------------------------------------------------------------------

local function formatPhraseList(phraseID, sPhrase)

return inMono(phraseID) .. ': ' .. sPhrase

end

--------------------------------------------------------------------------------

-- getSetID

--

-- Determines setID (expected either 'H' or 'P')

-- First route is: read |setid=

-- When |setid= is not set,

-- it looks for a first parameter that has an H of P prefix (in |P201|P202|...)

-- when not found, 'GHS' is retured

-- In one call, P and H numbers can *not* be mixed

-- so "|H201|P202|" will cause error "P202 not found" (... in H-list)

--------------------------------------------------------------------------------

local function getSetID(tArgs)

local setIDfound = 'GHS'

local paramsetID = tArgs['setid'] or nil

if (paramsetID ~= nil) and (paramsetID == 'P' or paramsetID == 'H') then

setIDfound = paramsetID

else

local initial = nil

for i, v in ipairs(tArgs) do

initial = mw.ustring.match(v, '^[PH]')

if initial ~=nil then

setIDfound = initial

break

end

end

end

return setIDfound

end

--------------------------------------------------------------------------------

-- getListType

--

-- Checks list format, including those from Module:List

--------------------------------------------------------------------------------

local function getListType(tArgs)

local listTypes = {

['abbr'] = true,

['bulleted'] = true,

['unbulleted'] = true,

['horizontal'] = true,

['ordered'] = true,

['horizontal_ordered'] = true,

['horizontal ordered'] = true,

['inline'] = true

}

local sListType = tArgs['listtype'] or 'abbr'

if sListType == '' or sListType == 'abbr' then

return 'abbr'

elseif listTypes[sListType] == true then

if sListType == 'horizontal ordered' then

sListType = 'horizontal_ordered'

end

return sListType

else

sListType = 'abbr'

end

return sListType

end

--------------------------------------------------------------------------------

-- getDoOmitRules

--------------------------------------------------------------------------------

local function getDoOmitRules(tArgs)

local b = yesno(tArgs['omit'], true)

if b == nil then b = true end

return yesno(b, true)

end

--------------------------------------------------------------------------------

-- prepareArgs

--

-- First: determine setID (from |setID= OR from prefixes in parameters)

-- Then: clean up & format phrase IDs (=unnamed parameters)

-- remove bad characters, create H/P pattern "H201", "P310+P302"

-- straight array, no nil's, sorted

--------------------------------------------------------------------------------

local function prepareArgs(tArgs)

tArgName['setID'] = getSetID(tArgs)

tArgName['listtype'] = getListType(tArgs)

tArgName['omit'] = getDoOmitRules(tArgs)

tArgs = tTools.compressSparseArray(tArgs) -- removes all named args

if string.len(tArgName['setID']) == 1 and #tArgs > 0 then

for i, v in ipairs(tArgs) do

v = mw.text.decode(v)

v = mw.ustring.gsub(v, '[^%d%+A-Za-z]', '')

v = mw.ustring.gsub(v, '^(%d)', tArgName['setID'] .. '%1')

v = mw.ustring.gsub(v, '%+(%d)', '+' .. tArgName['setID'] .. '%1')

tArgs[i] = v

end

table.sort(tArgs)

end

return tArgs

end

--------------------------------------------------------------------------------

-- listAll

--

-- Returns wikitable rows for each phrase id.

-- requires |setID=P/H

-- returns full list, all phrases, for a setID

-- 2-columns wikitable, sorted, sortable, anchor like "H201" for each

--------------------------------------------------------------------------------

function r.listAll(frame)

local newArgs = getArgs(frame)

local tL = {}

prepareArgs(newArgs)

local tRead

if tArgName['setID'] == 'H' then

tRead = GHSdata['Hphrases']

elseif tArgName['setID'] == 'P' then

tRead = GHSdata['Pphrases']

else

errorHPsetIDmissing()

return showPreviewMsg()

end

-- Intermediate table t2 to maintain order; read from original table (/data)

local t2 = {}

local iPh

for s, v in pairs(tRead) do

iPh = tonumber(mw.ustring.match(s, '[PH](%d%d%d)'))

if string.len(s) > 4 then

iPh = tTools.size(t2) + 1

end

table.insert(t2, iPh, s)

end

t2 = tTools.compressSparseArray(t2)

table.sort(t2)

local sTR, v, sAnchor

-- i = array index, s = phraseID, v = phrase text

for i, s in ipairs(t2) do

v = tRead[s]

sAnchor = ''

sTR = '|- ' .. sAnchor .. '\n| datasortvalue="' .. i .. '" | ' .. s .. ' || ' .. v

table.insert(tL, sTR)

end

return table.concat(tL, '\n')

end

--------------------------------------------------------------------------------

-- numberOfPhrases

--

-- Documentation

-- requires |setID=H/P

-- Returns number of phrases, in format

-- "GHS H-phrases (123)"

--------------------------------------------------------------------------------

function r.numberOfPhrases(frame)

local newArgs = getArgs(frame)

prepareArgs(newArgs)

local iT

if tArgName['setID'] == 'H' then

iT = tTools.size(GHSdata['Hphrases'])

elseif tArgName['setID'] == 'P' then

iT = tTools.size(GHSdata['Pphrases'])

else

errorHPsetIDmissing()

return showPreviewMsg()

end

return 'GHS ' .. PHlabel() .. ' (' .. tostring(iT) .. ')'

end

--------------------------------------------------------------------------------

-- listOmitRules

--

-- self-documentation

--------------------------------------------------------------------------------

function r.listOmitRules()

local tRules = GHSdata['tOmitRules']

local tL = {}

local s

s = wlHelpPage('Omit Rules')

.. ': when the keep ID is present, do not show the omit ID phrase'

table.insert(tL, s)

for keep, omit in pairs (tRules) do

s = '• keep ' .. inMono(keep) .. ', omit ' .. inMono(omit)

table.insert(tL, s)

end

return table.concat(tL, '
')

end

--------------------------------------------------------------------------------

-- _main

--

-- processes setID (H, P) and phrase codes

-- error: setID not P, H

-- code not found

-- cannot mix H and P phrases

-- reads phrases from /data H or P phrases tables

-- formats phrase (abbreviation, abbr-title, phraseID)

--------------------------------------------------------------------------------

function r._main(tArgs)

tArgs = prepareArgs(tArgs)

if #tArgs == 0 then

return showPreviewMsg() -- no content

elseif tArgName['setID'] == 'GHS' then

return errorHPsetIDnotFound()

end

tArgs = applyRemoveDuplicates(tArgs)

if tArgName['omit'] then

tArgs = applyOmitRules(tArgs)

end

local formatterF

if tArgName['listtype'] == 'abbr' then

formatterF = formatPhraseAbbr

elseif tArgName['listtype'] == 'inline' then

formatterF = formatPhraseInline

else --- Module:List options

formatterF = formatPhraseList

end

local tReadD = {}

if tArgName['setID'] == 'H' then

tReadD = GHSdata['Hphrases']

elseif tArgName['setID'] == 'P' then

tReadD = GHSdata['Pphrases']

else

return showPreviewMsg()

end

local sPhrase

local tR = {}

for i, v in ipairs(tArgs) do

sPhrase = tReadD[v]

if sPhrase == nil then

table.insert(tR, errorPhraseIDnotFound(tostring(v)))

else

table.insert(tR, formatterF(v, sPhrase))

end

end

if tArgName['listtype'] == 'abbr' then

return table.concat(tR, ', ') .. showPreviewMsg()

elseif tArgName['listtype'] == 'inline' then

return table.concat(tR, ', ') .. showPreviewMsg()

else

local mList = require('Module:List')

return mList[tArgName['listtype']](tR) .. showPreviewMsg()

end

end

--------------------------------------------------------------------------------

-- main

--

-- handles template input frame, then calls generic _main() function

-- To be invoked from {{template}}

--------------------------------------------------------------------------------

function r.main(frame)

local newArgs = getArgs(frame)

return r._main(newArgs)

end

return r