Module:ExperimentalNavplate

--[[This Mess is an experimental module for seeing if we can create a navplate that populates itself as an ordered, nested list.

It works by getting all of the pages from the category (in this example, Stanton) including their infobox data through a single DPLlua request. It then parses these into categories and assigns a sort order. These lists are then iterated through to find the objects that orbit the star, objects that orbit those objects, etc. Those lists are then sorted by their orbit location (a new required field) and then semantic precedence defined by a precedence score. As this sorting goes on, a table of strings is built up to generate the page contents.]]--

local p = {};   --Necessary Lua Object DO NOT remove! local abbreviations = {{},{}} -- empty container for list of abbreviations (full length and cleaned up) local abbreviationsCheckThreshold = 5; local dpl = require( 'Module:DPLlua' )

--List of possible categories for each location ordered : --Category Name, Clean version, Symbol, Important?, Precedence Score --Mistakes/inadequacies arising from the code are likely to originate here! --ORDER of the list defines priority for semantic collapsing: more specific classifications should go to the --top, more general ones to the bottom (e.g. Gas Giant to top, Planet to bottom) --PRECEDENCE SCORE is for sorting lists

local typelist = {

--GAS PLANETS {"Ice Giant","gasgiant","🪐",true,1}, {"Gas Dwarf","gasgiant","🪐",true,1}, {"Puffy Planet","gasgiant","🪐",true,1}, {"Gas Giant","gasgiant","🪐",true,1}, {"Super-Jupiter","superjupiter","🪐",true,1}, --This is more of a size class than an environment class so I am putting it below Gas Giant, and we won't see it.

--TERRESTRIAL PLANETS (would prefer if each planet had an environmental description) {"Ice Planet","iceplanet","🪐",true,1}, {"Iron Planet","ironplanet","🪐",true,1}, {"Smog Planet","smogplanet","🪐",true,1}, {"Carbon Planet","carbonplanet","🪐",true,1}, {"Chthonian Planet","chthonianplanet","🪐",true,1}, {"Lava Planet","lavaplanet","🪐",true,1}, {"Ocean Planet","oceanplanet","🪐",true,1}, {"Artificial Planet","artificialplanet","🪐",true,1}, {"Protoplanet","protoplanet","🪐",true,1}, {"Terrestrial Rocky","terrestrialrocky","🪐",true,1}, --These are commonly featured, but don't fit with the others and are less informative {"Super-Earth","superearth","🪐",true,1}, --This is more of a size class than an environment class {"Coreless Planet","corelessplanet","🪐",true,1}, --A geological feature, not an environment. {"Dwarf Planet","dwarfplanet","🪐",true,1}, --This is more of a size class than an environment class {"Mesoplanet","mesoplanet","🪐",true,1}, --This is more of a size class than an environment class

--IDEALLY THIS SHOULD NOT BE USED, AND A MORE DESCRIPTIVE CATEGORY ASSIGNED {"Planet","planet","🪐",true,1.9}, --OTHER CELESTIAL {"Jump Point","jumppoint","🌌", true,99}, {"Moon","moon","🌙",true,5}, {"Nebula","nebula","☁️",true,5.5}, --Probably major??

--ASTEROIDS {"Planetoid","planetoid","🪐",true,6}, {"Asteroid Belt","asteroidbelt","⭕",true,6}, {"Asteroid Ring","asteroidring","⭕",true,6}, {"Asteroid Field","asteroidfield","🧱",false,6}, {"Large Asteroid","largeasteroid","🧱",false,6}, {"Asteroid Cluster","asteroidcluster","🧱",false,7}, {"Asteroid","asteroid","🧱",false,7.9},

--SPACE STATIONS {"Flotilla","flotilla","🛳️",true,4}, {"Trade Hub","tradehub","💰",true,4}, {"Rest Stop","reststop","",false,5}, {"Military","military","⚔️",true,4}, -- Can use as a catch-all for military types, though will apply to both land and space (clear from context?) {"Outlaw","outlaw","☠️",true,4}, -- Can use as a catch-all for outlaw types, though will apply to both land and space (clear from context?) {"Comm Array","commarray","", false,6}, {"Space Station","spacestation","🚀", false,6.9}, {"Satellite","satellite","🛰️", false,6.9}, --MISC {"Contact","contact","⛳",false,8}, {"Navigation Point","navigationpoint","🧱",false,8}, {"Lagrange Point","lagrangepoint","☮️",false,5.5}, {"Cardinal Point","cardinal","🧭",false,8}, {"Quantum Trace Point","quantumtrace","🎯",false,8}, {"Anomaly","anomaly","🔬",false,8},

--SETTLEMENTS {"City","city","🌆️",true,0}, {"Settlement","settlement","🏘️️",true,0}, {"Asteroid Base","asteroidbase","🗿",true,4.5}, {"Landing Zone","landingzone", "✈",true,0}, {"Space Port","spaceport","⚓",true,1}, --CITY AMENETIES {"Apartment Habs","apartment","🛏️",false,2}, {"Bar","bar","🥂️",false,2}, {"Commercial Building","commercial","💱", false,2}, {"Convention Center","convention","🏟️", false,2}, {"Shop","shop","🛒 ", false,2}, {"Hospital","hospital","⚕️", false,2},

--LANDMARKS {"Cave","cave","🕸️", false,2}, {"Drug Lab","druglab","💉", false,2}, {"Farming Outpost","farming","🚜️", false,2}, {"Mining Outpost","mining","⛏️️", false,2}, {"Salvage Yard","salvage","♻️️", false,2}, {"Shelter","shelter","☂️️", false,2}, {"Stash","stash","🔒️", false,2}, {"Underground Facility","underground","🔽️", false,2}, {"Outpost","outpost","🏠", false,3}, {"Landmark","landmark","🏠", false,3} }

--Special cases for the star, black hole and unidentified. local starSymb = "☀" local blackholeSymb = "🕳️" local miscLandSymb = "🏚" local miscSpaceSymb = "🌠" local orphanSymbol = "?" --How small to make the minor location expandables. local minorlocationfontsize = 60

condenseByType = function(lines,unimportant) local originalCount = #unimportant for i,typ in ipairs(typelist) do		-- we only want to condense minor types if typ[4] == false then local rest = {} local found = {} local count = 0 for j, loc in ipairs(unimportant) do				if string.find(loc["typeClean"],typ[2])~=nil or string.find(loc["classClean"],typ[2])~=nil then count = count+1 table.insert(found,loc) else table.insert(rest,loc) end end if count == originalCount then break end if count>1 then unimportant = rest table.insert(lines,"\n\n"..typ[1].." ("..count..") \n") table.insert(lines,"\n") --Can still run a condenseByName here in addition local update = condenseByName(lines,found) lines=update[1] found=update[2] for k,item in ipairs(found) do					table.insert(lines,"\n	"..item["ListEntry"]) table.insert(lines,"\n ") end table.insert(lines,"\n ") table.insert(lines,"\n ") table.insert(lines,"\n ") end end end --If still over threshold, run the remaining list through name condensation if #unimportant>abbreviationsCheckThreshold then return condenseByName(lines,unimportant) else --Else just return what we have return{lines,unimportant} end end

condenseByName = function(lines,unimportant) local originalCount = #unimportant for i,abb in ipairs(abbreviations[2]) do		local rest = {} local found = {} local count = 0 for j,loc in ipairs(unimportant) do			if string.find(loc["CleanName"],abb)~=nil then count = count+1 table.insert(found,loc) else table.insert(rest,loc) end end if count == originalCount then break end

if count>1 then unimportant = rest table.insert(lines,"\n\n"..abbreviations[1][i].."... ("..count..") \n") table.insert(lines,"\n") for k,item in ipairs(found) do				table.insert(lines,"\n	"..item["ListEntry"]) table.insert(lines,"\n ") end table.insert(lines,"\n ") table.insert(lines,"\n ") table.insert(lines,"\n ") end end return {lines,unimportant} end

populateEntry = function(category,entry,loctype,name) local class = "item" if loctype == "star" then class = "heading" end entry["ListEntry"] = ""..category[3].." "..name.." " entry["Important"] = category[4] entry["SortPower"] = category[5] return entry end

iterateToPopulate = function(lines,children,rest,expand)

for i=1 ,2, 1 do		local obj = children[i] local important = obj[1] local unimportant = obj[2] if important ~= nil then table.sort(important,compareObjects) for j,loc in ipairs(important) do				table.insert(lines,"\n"..loc["ListEntry"]) local locChl = findChildren(rest,loc["ProperName"]) rest=locChl[3] if #locChl[1][1]+#locChl[1][2]+#locChl[2][1]+#locChl[2][2]>0 then table.insert(lines,"\n") local update = iterateToPopulate(lines,locChl,rest,expand) lines=update[1] rest=update[2] table.insert(lines,"\n ") end table.insert(lines,"\n ") end end if unimportant~= nil then if #unimportant>0 then if expand == false then table.insert(lines,"\n\n"..#unimportant.." minor locations \n") end if #unimportant>=abbreviationsCheckThreshold then local returned = condenseByType(lines,unimportant) lines = returned[1] unimportant = returned[2] end --This typesets the residual entries for j, loc in ipairs(unimportant) do 					table.insert(lines,"\n	"..loc["ListEntry"]) local locChl = findChildren(rest,loc["ProperName"]) rest=locChl[3] if #locChl[1][1]+#locChl[1][2]+#locChl[2][1]+#locChl[2][2]>0 then table.insert(lines,"\n") local update = iterateToPopulate(lines,locChl,rest,true) lines=update[1] rest=update[2] table.insert(lines,"\n ") end table.insert(lines,"\n ") end if expand == false then table.insert(lines,"\n ") end table.insert(lines,"\n ") end end end local ret = {lines,rest} return ret end

--Find all objects in list that mention string s in their location descriptor. Returns in format: --{{important land, unimportant land},{important space},{unimportant space},{remainder]} findChildren = function (list, s)	local found={{{},{}},{{},{}},{}} s = string.lower(s) s = s:gsub("%s+", "") s = s:gsub("%p+", "")

local brack = ""..s.."" for x, item in ipairs(list) do		loc = item["Location"] orb = item["Orbits"] if loc == nil then loc = "NOTFOUND" end if orb == nil then orb = "NOTFOUND" end loc = string.lower(loc) loc = loc:gsub("%s+", "") loc = loc:gsub("%p+", "") orb = string.lower(orb) orb = orb:gsub("%s+", "") orb = orb:gsub("%p+", "") if string.find(loc,s)~=nil or string.find(loc,brack)~=nil or string.find(orb,s)~=nil or string.find(orb,brack)~=nil then local important = 2 local space = 2 if item["inSpace"] == true then land = 1 end if item["Important"] == true then important = 1 end table.insert(found[land][important],item) else table.insert(found[3],item) end end return found end

--Return a table that combines mergeTab = function (table1, table2) local newtab = {} for x,item in ipairs(table1) do		table.insert(newtab,table1[i]) end for x,item in ipairs(table2) do		include = true for y,item2 in ipairs(table1) do			if item == item2 then include = false break end end if include then table.insert(newtab,table2[i]) end end return newtab end

--Sort objects by Orbital Position, if not (absent data or idential position) -- then Precedence, if not then alphabetically. compareObjects = function (a,b) orbA = a["Orbital Position"] orbB = b["Orbital Position"] if orbA == nil or orbB == nil or orbA == orbB then if a["SortPower"] == b["SortPower"] then return a["ProperName"]<b["ProperName"] else return a["SortPower"] < b["SortPower"] end else return orbA<orbB end end

p.showTable = function (frame) local lines = {} table.insert(lines,"{|\n!Category\n!Icon\n!Important\n!Priority") for i,ln in ipairs(typelist) do		table.insert(lines,"\n|-\n|"..ln[1].."\n|"..ln[3].."\n|"..tostring(ln[4]).."\n|"..ln[5]) end table.insert(lines,"\n|}") return table.concat(lines) end

--MAIN FUNCTION --Takes the system from args (defaults to stanton if it cannot) p.fullplate= function(frame) local searchCategory = frame.args[1] local abbrevs = frame.args[2] if searchCategory == "" or searchCategory == nil then return "Looks like the search category was set up incorrectly" end if abbrevs ~= null then for abb in abbrevs:gmatch("([^,]+)") do			table.insert(abbreviations[1],abb) abb = string.lower(abb) abb = abb:gsub("%s+", "") abb = abb:gsub("%p+", "") table.insert(abbreviations[2],abb) end end

-- Get the pages from the category of Stanton System -- Potential to make this more elegant local list = dpl.ask({		namespace = '',		category = searchCategory,		include='{Infobox Astronomical Object},{Infobox Location},{Infobox Space Station}'	} )

local stars={} local objects = {} local orphans = {} --pages without the infobox local infoboxtypes = {true,false,true} --Iterate through list, categorise and populate accordingly for i, line in ipairs(list) do		local entry = {} local name = line["title"] local loctype = "orphan" local inspace = true for j, cat in ipairs(infoboxtypes) do			entry = line["include"][j] if type(entry)== "table" then entry["ProperName"]= name local clean = string.lower(name) clean = clean:gsub("%s+", "") clean = clean:gsub("%p+", "") entry["CleanName"]=clean loctype = entry["Type"] classif = entry["Classification"]

entry["inSpace"] = cat entry["SortPower"] = 9999 break end end loctype = string.lower(loctype) loctype = loctype:gsub("%s+", "") loctype = loctype:gsub("%p+", "") entry["typeClean"] = loctype if classif ~= nil then classif = string.lower(classif) classif = classif:gsub("%s+", "") classif = classif:gsub("%p+", "") entry["classClean"] = classif else entry["classClean"] = "" end if loctype =="star" then entry["ListEntry"] = "<div class=\"locationnavplate-star locationnavplate-item\"><span class=\"locationnavplate-item-icon\">"..starSymb.." <span class=\"locationnavplate-item-name\">"..name.." " table.insert(stars,entry) entry["ProperName"] = line["title"] elseif loctype =="star" then entry["ListEntry"] = "<div class=\"locationnavplate-star locationnavplate-item\"><span class=\"locationnavplate-item-icon\">"..starSymb.." <span class=\"locationnavplate-item-name\">"..name.." " table.insert(stars,entry) entry["ProperName"] = line["title"] elseif loctype == "orphan" then table.insert(orphans,orphanSymbol..name) else entry["ProperName"] = line["title"] local found = false --Check Classification first, as this is often more specific if classif ~= nil then for j, category in ipairs(typelist) do					if string.find(classif,category[2])~=nil then entry = populateEntry(category,entry,classif,name) table.insert(objects,entry) found = true break; end end end --If classification doesn't give a match, then match type if found== false then for j, category in ipairs(typelist) do					if string.find(loctype,category[2])~=nil then entry = populateEntry(category,entry,loctype,name) table.insert(objects,entry) found = true break; end end end --If all else fails, give it a generic land or space class if found == false then if entry["inSpace"] == true then entry["ListEntry"] = "<div class=\"locationnavplate-miscspace locationnavplate-item\"><span class=\"locationnavplate-item-icon\">"..miscSpaceSymb.." <span class=\"locationnavplate-item-name\">"..name.." " else entry["ListEntry"] = "<div class=\"locationnavplate-miscland locationnavplate-item\"><span class=\"locationnavplate-item-icon\">"..miscLandSymb.." <span class=\"locationnavplate-item-name\">"..name.." " end entry["Important"] = false entry["SortPower"] = 10 table.insert(objects,entry) end end end local lines = {} table.insert(lines,"<div class=\"locationnavplate floatnone\" role=\"navigation\">\n<div class=\"locationnavplate-header\">Locations in<div class=\"locationnavplate-source\">"..searchCategory.." \n ") if star == {} and searchCategory == "Min System" then --Edge case, starless system for i, loc in objects do			if loc["ProperName"] == "Min I" then table.insert(star,loc) table.remove(objects,i) break end end end

if star == {} then table.insert(lines,"Star Not Properly identified in infobox. You can help by fixing this.") table.insert(lines,"\n<div class=\"locationnavplate-item-group\">") update = iterateToPopulate(lines,objects,{},false) lines=update[1] objects=update[2] else table.sort(stars,compareObjects) table.insert(lines,stars[1]["ListEntry"]) local orbitSun = findChildren(objects,stars[1]["ProperName"]) objects = orbitSun[3] for i=2,#stars,1 do			table.insert(lines," \n"..stars[i]["ListEntry"]) orbitSun = mergeTab(orbitSun,findChildren(objects,stars[i]["ProperName"])) end table.insert(lines,"\n<div class=\"locationnavplate-item-group\">") if #orbitSun > 0 then update = iterateToPopulate(lines,orbitSun,objects,false) lines=update[1] objects=update[2] else table.insert(lines,"\nCannot identify Objects orbiting star") update = iterateToPopulate(lines,objects,{},false) lines=update[1] objects=update[2] end table.insert(lines,"\n ") table.insert(lines,"\n ") end if #objects>0 then table.insert(lines,"<div class=\"error\">\nThe following locations are not formatting correctly, either because their infoboxes need updating, or due to an error. You can help by editing the wiki.(link to specific article about this problem)") table.sort(objects,compareObjects) for i, obj in ipairs(objects) do table.insert(lines,"\n".. obj["ListEntry"].." ") end table.insert(lines,"\n ")

end if #orphans>0 then table.insert(lines,"<div class=\"error\">\nThe following locations are not formatting correctly because they do not have infoboxes. You can help by providing them with one. (link to specific article about this problem)") for i, orph in ipairs(orphans) do table.insert(lines,"\n".. orph.." ") end table.insert(lines,"\n ") end

table.insert(lines,"\n ")

return table.concat(lines) end

return p