Module:Sweden by county category navigation

--[[

v01.18: fix handling of definite article (e.g. for 'the Northwest Territories')

2.0 Resolve category redirects

each title consists of 3 parts

* prefix

* county name

* suffix

e.g. "Foo in Västra Götaland County"

* prefix = "Foo in "

* county name = "Västra Götaland County"

* suffix = ""

e.g. "Stockholm County-related lists"

* prefix = ""

* county name = "Stockholm County"

* suffix = "-related lists"

]]

-- config

local textSize = '90%'

local tableClass="toc"

local evenRowStyle = "vertical-align:top; background-color:#f3f3f3;"

local oddRowStyle = "vertical-align:top;"

local labelStyle = "text-align:right; font-weight: bold; padding: 0.25em 0.5em 0.25em 0.5em;"

local listStyle = "text-align:left; font-weight: normal; padding: 0.25em 0.5em 0.25em 0.5em;"

local greyLinkColor = "#888"

--[[ Note that the table styles are designed to ensure that the navbox is as wide as possible, while still leaving

enough enough space on the right for portal boxes, commons links, and GeoGroup templates.

A lot of fiddling was needed to make it work, so please test any chnages very carfully in the sandbox.

]]

local tableFallbackMaxWidth="auto"

local tableMaxWidth="calc(100% - 25em)" -- Template:GeoGroup has width: 23em<

-- local tableStyle="margin-left:0; margin-right:auto; clear:left !important; margin-top:0 !important; float:left; width:auto;"

local tableStyle="margin-left:0; margin-right:auto; clear:left !important; margin-top:0 !important; width:auto;"

-- Templates which are allowed to call this module

local callingTemplates = {

'Template:Sweden by county category navigation',

}

-- globals for this module

local debugging = false

local debugmsg = ""

local tableRowNum = 0

local title_prefix = ""

local title_suffix = ""

local title_prefix

local title_suffix

local thisPageCounty

local greyLinkCount = 0

local blueLinkCount = 0

local parentname = ""

local templateName

local horizontal = require('Module:List').horizontal

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

local yesno = require('Module:Yesno')

local ResolveCategoryRedirect = require('Module:Resolve category redirect').rtarget

local p = {}

--[[

Plain text list of counties

* Each entry exactly as it appears in running text in category titles, with any prefix (e.g. "the")

* Be sure to avoid hidden characters and duplicate spaces. They break the pattern-matching on which this module relies

]]

local SwedenCounties = {

'Blekinge County',

'Dalarna County',

'Gävleborg County',

'Gotland County',

'Halland County',

'Jämtland County',

'Jönköping County',

'Kalmar County',

'Kronoberg County',

'Norrbotten County',

'Örebro County',

'Östergötland County',

'Skåne County',

'Södermanland County',

'Stockholm County',

'Uppsala County',

'Värmland County',

'Västerbotten County',

'Västernorrland County',

'Västmanland County',

'Västra Götaland County'

}

-- If the page title matches any of these Lua patterns, treat it as a false positive

local falsePositiveChecks = {

}

local function publishDebugLog()

if not debugging then

return ""

end

return "==Debugging ==\n\n" .. debugmsg .. "\n== Output ==\n"

end

-- debugLog builds a log which can be output if debuging is enabled

-- each log entry is given a level, so that the output is not simply a flat list

-- a debug msg may be appended to the previous msg by setting the level to nil

local function debugLog(level, msg)

if (debugmsg == nil) then

debugmsg = ""

end

if (level ~= nil) then

-- not appending, so make a new line

debugmsg = debugmsg .. "\n"

-- then add the level

local i

for i = 1, level do

if (i % 2) == 1 then

debugmsg = debugmsg .. "#"

else

debugmsg = debugmsg .. "*"

end

end

end

debugmsg = debugmsg .. " " .. msg

return true

end

local function makeTrackingCategory()

-- discount the current page, which will always be coded as a blue link, but rendered as bold un-navigable

blueLinkCount = blueLinkCount - 1

if greyLinkCount == 0 then

return "Category:" .. templateName .. " with no grey links"

end

if blueLinkCount == 0 then

return "Category:" .. templateName .. " with all grey links"

end

if greyLinkCount <= 2 then

return "Category:" .. templateName .. " with 1–2 grey links"

end

if greyLinkCount <= 5 then

return "Category:" .. templateName .. " with 3–5 grey links"

end

if greyLinkCount > 15 then

return "Category:" .. templateName .. " with 16 or more grey links"

end

if greyLinkCount > 5 then

return "Category:" .. templateName .. " with 6–15 grey links"

end

return ""

end

-- Make a piped link to a category, if it exists

-- If it doesn't exist, just display the greyed the link title without linking

local function makeCatLink(catname, disp)

local displaytext

if (disp ~= "") and (disp ~= nil) then

-- use 'disp' parameter, but strip any leading word "the"

displaytext = mw.ustring.gsub(disp, "^[tT]he%s+", "");

else

displaytext = catname

end

local fmtlink

local catPage = mw.title.new( catname, "Category" )

if (catPage.exists) then

fmtlink = "" .. displaytext .. ""

blueLinkCount = blueLinkCount + 1

else

fmtlink = '' .. displaytext .. ""

greyLinkCount = greyLinkCount + 1

end

return fmtlink

end

local function makeCatName(countyName, prefix, suffix)

local thisCatName = prefix .. countyName .. suffix

debugLog(5, "thisCatName = [" .. thisCatName .. "]")

--[[

Now check whether the all following conditions are true

1/ the countyName begins with "the"

2/ the category does NOT exist if we use "the "

3/ the category does exist if we strip "the "

If those conditions are all true, them strip "the"

]]

if (mw.ustring.match(countyName, "^[tT]he ") ~= nil) then

debugLog(6, "CountyName begins with 'the'")

local countyNameStripped = mw.ustring.gsub(countyName, "^[tT]he ", "", 1)

local thisCatNameStripped = prefix .. countyNameStripped .. suffix

debugLog(6, "thisCatNameStripped = [" .. thisCatNameStripped .. "]")

local testCatPage = mw.title.new(thisCatName, "Category" )

local testCatPageStripped = mw.title.new(thisCatNameStripped, "Category" )

if not testCatPage.exists then

debugLog(7, "[" .. testCatPage.fullText .. "] .. does not exist")

if (testCatPageStripped.exists) then

debugLog(7, "[" .. testCatPageStripped.fullText .. "] .. DOES exist, so use that")

return thisCatNameStripped

end

end

end

return thisCatName

end

local function makeTableRow(rowLabel, countyList)

debugLog(2, "makeTableRow, label: ")

if (rowLabel == nil) then

rowLabel = "By county"

debugLog(nil, rowLabel)

else

rowLabel = mw.text.trim(rowLabel)

debugLog(nil, " [" .. rowLabel .. "]")

end

tableRowNum = tableRowNum + 1

local thisRow

if (tableRowNum % 2) == 0 then

debugLog(3, "Even-numbered")

thisRow = '\n'

else

debugLog(3, "Odd-numbered")

thisRow = '\n'

end

if not ((rowLabel == nil) or (rowLabel =="")) then

thisRow = thisRow .. '' .. rowLabel .. '\n'

end

-- now begin making the row contents

debugLog(3, "Process countyList")

local list_args = {}

for i, aCounty in ipairs(countyList) do

debugLog(4, "No. " .. tostring(i) .. ": [" .. aCounty .. "]")

myCatName = ResolveCategoryRedirect(makeCatName(aCounty, title_prefix, title_suffix))

table.insert(list_args, makeCatLink(myCatName, aCounty))

end

return thisRow .. '' .. horizontal(list_args) .. '\n'

end

local function makeTable()

debugLog(1, "makeTable")

tableRowNum = 0

local myTable = '

myTable = myTable .. ' style="' .. tableStyle .. '; font-size:' .. textSize .. '; max-width:' .. tableFallbackMaxWidth .. '; max-width:' .. tableMaxWidth ..'">\n'

myTable = myTable .. makeTableRow(nil, SwedenCounties)

myTable = myTable .. "

\n"

return myTable

end

local function patternSearchEncode(s)

return mw.ustring.gsub(s, "([%W])", "%%%1")

end

-- Does the pagename include a county name?

local function findcountyNameInPagename(pn, countyList, description)

local i, aCounty, testCounty

debugLog(2, "checking [" .. pn .."] for a county name in county set: " .. description)

for i, aCounty in ipairs(countyList) do

testCounty = aCounty

debugLog(3, "testing: [" .. testCounty .. "]")

local testCountyEncoded = patternSearchEncode(testCounty)

-- For efficiency, the first test is a simple match as a a screening test

-- If the bare county name is nowhere in the pagename, then no need for

-- more precise checks

-- This check would be one line in regex, but Lua pattern matching is cruder,

--so we need several passes to ensure that any match is of a complete word

debugLog(4, "simple match? ")

if (not mw.ustring.match(pn, testCountyEncoded)) then

debugLog(nil, "Fail")

else

debugLog(nil, "Success")

-- test for false positives

local j, aFalsePositiveTest

for j, aFalsePositiveTest in ipairs(falsePositiveChecks) do

debugLog(5, "false positive test pattern '" .. aFalsePositiveTest .. "' ? ")

if (mw.ustring.match(pn, aFalsePositiveTest)) then

debugLog(nil, "Match, so fail")

return nil

end

debugLog(nil, "No match, so OK")

end

debugLog(4, "match whole name? ")

if (pn == testCounty) then

debugLog(nil, "Yes")

return testCounty

end

debugLog(nil, "No")

debugLog(4, "match at start, followed by separator? ")

if mw.ustring.match(pn, "^" .. testCountyEncoded .. "[^%w]") then

debugLog(nil, "Yes")

return testCounty

end

debugLog(nil, "No")

debugLog(4, "match at end, preceded by separator? ")

if mw.ustring.match(pn, "[^%w]" .. testCountyEncoded .. "$") then

debugLog(nil, "Yes")

return testCounty

end

debugLog(nil, "No")

debugLog(4, "match anywhere, preceded and followed by separator? ")

if mw.ustring.match(pn, "[^%w]" .. testCountyEncoded .. "[^%w]") then

debugLog(nil, "Yes")

return testCounty

end

debugLog(nil, "No")

end

-- Special case: if the county name we are testing begins with a prefixed "the"

debugLog(4, "does testCounty begin with 'the' ? ")

if (mw.ustring.match(testCounty, "^[tT]he ") == nil) then

debugLog(nil, "No")

else

debugLog(nil, "Yes")

end

if (mw.ustring.match(testCounty, "^[tT]he ") ~= nil) then

local testCountyStripped = mw.ustring.gsub(testCounty, "^[tT]he ", "", 1)

local testCountyStrippedEncoded = patternSearchEncode(testCountyStripped)

debugLog(4, "test pattern without leading definite article, i.e. '" .. testCountyStrippedEncoded .. "' ? ")

if (mw.ustring.match(pn, "[^%w]" .. testCountyStrippedEncoded .. "[^%w]") ~= nil)

or (mw.ustring.match(pn, "^" .. testCountyStrippedEncoded .. "[^%w]") ~= nil)

or (mw.ustring.match(pn, "[^%w]" .. testCountyStrippedEncoded .. "$") ~= nil)

or (mw.ustring.match(pn, "^" .. testCountyStrippedEncoded .. "$") ~= nil) then

debugLog(nil, "Yes")

return testCountyStripped

end

debugLog(nil, "No")

end

end

return nil

end

-- parse the pagename to find 3 parts: prefix, county name, suffix

local function parsePagename(pn)

debugLog(1, "parsePagename: [" .. pn .. "]")

local validcountyName

validcountyName = findcountyNameInPagename(pn, SwedenCounties, "countylst")

if validcountyName == nil then

return false

end

-- if we get here, the page name "pn" includes a validcountyName

-- so now we need to split the string

debugLog(2, "split pagename around [" .. validcountyName .. "]")

local validCountyEncoded = mw.ustring.gsub(validcountyName, "([%W])", "%%%1")

match_prefix, match_county, match_suffix = mw.ustring.match(pn, "^(.*)(" .. validCountyEncoded .. ")(.*)$")

title_prefix = match_prefix

title_suffix = match_suffix

debugLog(2, "parse successful")

debugLog(3, "title_prefix = [" .. title_prefix .. "]")

debugLog(3, "thisPageCounty = [" .. match_county .. "]")

debugLog(3, "title_suffix = [" .. title_suffix .. "]")

return true

end

local function getYesNoParam(args, thisParamName, defaultVal)

local paramVal = args[thisParamName]

if paramVal == nil then

paramVal = ""

end

debugLog(2, "Evaluate yes/no parameter: [" .. thisParamName .. "] = [" .. paramVal .. "]")

debugLog(3, "default = " .. ((defaultVal and "Yes") or "No"))

debugLog(3, "Evaluate as: ")

local returnValue

if paramVal == "" then

returnValue = defaultVal

else

returnValue = yesno(args[thisParamName], defaultVal)

end

if (returnValue) then

debugLog(nil, "Yes")

else

debugLog(nil, "No")

end

return returnValue

end

local function makeErrorMsg(s)

return '

' .. parentname .. ' Error: ' .. s .. '

\n'

end

local function isValidParent(p)

for i, aParent in ipairs(callingTemplates) do

if p == aParent then

return true

end

end

return false

end

function p.main(frame)

local parent = frame:getParent()

if parent then

parentname = parent:getTitle():gsub('/sandbox$', '')

end

if (parentname == nil) or not isValidParent(parentname) then

local errormsg = '

Error: ' .. parentname .. ' is not a valid wrapper for ' .. frame:getTitle() .. '\n'

errormsg = errormsg .. '

Valid wrappers: '

local i, aParent

for i, aParent in ipairs(callingTemplates) do

errormsg = errormsg .. '' .. aParent .. ''

end

errormsg = errormsg .. '

'

return errormsg

end

templateName = mw.ustring.gsub(parentname, "^Template:", "")

debugLog(1, "Check parameters")

debugging = getYesNoParam(frame.args, "debug", false)

-- get the page title

thispage = mw.title.getCurrentTitle()

thispagename = thispage.text;

debugLog(1, "mw.title.getCurrentTitle()")

debugLog(2, "thispage.text = [" .. thispage.text .."]")

debugLog(2, "thispage.namespace = [" .. thispage.namespace .."]")

debugLog(2, "thispage.nsText = [" .. thispage.nsText .."]")

debugLog(2, "is it a cat? using (thispage:inNamespace(14)): ")

if not (thispage:inNamespace(14)) then

debugLog(nil, "No, this is not a category")

debugLog(1, "Not a category, so no output")

return makeErrorMsg("only for use on a category page") .. publishDebugLog()

end

debugLog(nil, "Yes, this is a category")

if not parsePagename(thispagename) then

-- some error parsing the title, so don't proceed to output

local trackingCatInvalid = "Category:" .. templateName .. " on invalid category"

return makeErrorMsg('the name of this category does not include a valid Swedish county') .. publishDebugLog() .. trackingCatInvalid

end

debugLog(1, "all parse done")

debugLog(2, "title_prefix = [" .. title_prefix .. "]")

debugLog(2, "title_suffix = [" .. title_suffix .. "]")

local myNavTable = makeTable()

debugLog(2, "blueLinkCount = [" .. blueLinkCount .. "].   (NB The current page is always counted as a bluelink, but will not be navigable)")

debugLog(2, "greyLinkCount = [" .. greyLinkCount .. "]")

if (blueLinkCount <= 1) then

-- This is a navbar to nowhere, so suppress display

myNavTable = ""

debugLog(1, "Zero bluelinks (other than the current page) makes this a navbox to nowhere, so do not display the navbox")

end

local myTrackingCat = makeTrackingCategory()

return publishDebugLog() .. myNavTable .. myTrackingCat

end

return p