Module:MaroScaleSandbox

From MTG Wiki
Revision as of 23:22, 3 July 2022 by >Jerodast (some comments)
Jump to navigation Jump to search

This is a sandbox page for experimenting with the Module:MaroScale lua script. Click for /doc access.

Goals:

  • Learn a bit more how it's used/about Modules
  • Make some updates/improvements (see script comments for details)

User:jerodast (talk) will request to delete this page once any useful changes are merged to core MaroScale page.

Since this module gets arguments from frame:getParent(), its use is best exemplified / tested via the calling template. These examples don't use real references, which in practice would be true citations with a full reference footnote. In these examples, ratings will always be EXPECTED to be in ascending order once sorted by date, to easily verify correctness.

Let's put[a] in some[b] fake footnotes to test compatibility of tables & other footnotes.[c]

PlaneLatest rankingTimeline
Innistrad1
  • 1[4]
Ravnica[d]1
  • 1[4]
  1. First fake footnote
  2. Second
  3. Final fake footnote!
  4. Worst plane ever
MechanicLatest rankingTimeline
-1/-1 counters2
  • 1[4]
  • 2[4]
Grind6
  • 5[4]
  • 6[4]
Rhystic9
  • 9[4]
Creature typeLatest rankingTimeline
Beeble10
  • 8[4]
  • 10[4]
Dauthi6
  • 6[4]
Djinn4
Dragon1
Elf1
Goblin1
Human1
Hydra1
Werewolf3
PlaneswalkerLatest rankingTimeline
Kiora4
  • 4[4]
Nahiri4
  • 3[4]
  • 4[4]
Tibalt7
  • 5[4]
  • 6[4]
  • 7[4]
Urza9
  • 9[4]

Quick links for live pages:

Lua error at line 191: attempt to compare nil with string.

Footnotes


--[[ Sandbox page for the MaroScale lua script

jerodast will request to delete this page once any useful changes are merged to
  the live MaroScale page

Goals for jerodast:
  * learn a bit more how it's used, AND about Modules in general
  * update LEGAL_EXPANSIONS
  * allow for not-a-single-number phrasings to be given an approximate number
      for sorting but still display, e.g. "about a 9" (treated as 9) or
      "7 or 8" (treated as 7.5)
  * explore possibility of having dates show up in the list of ratings changes,
      since the current simple serial list presentation makes ratings appear
      uniformly spaced when they're not
  * add column for additional notes - note that Beeble Scale crams notes into
      Timeline column already
]]

--[[ Beeble scale currently includes notes simply by placing them after the ref
     part of an entry:
    
	| entry25  = Phelddagrif
	| ratings25= {9,2018-10-21,<ref>{{EzTumblr|http://markrosewater.tumblr.com/post/179292907638/|title=Where is Phelddagriff on the Beeble Scale?|October 21, 2018}}</ref>}, 
	{9,2018-11-26,<ref>{{EzTumblr|http://markrosewater.tumblr.com/post/180506854918/|title=Where are phelddagrifs on the beeble scale? |November 26, 2018}}</ref> (actually 9 ½.)},
	
	Shows as:           9[32], 9[33] (actually 9 ½.)
	
	
	| entry4   = Dwarf
	| ratings4 = {1,2013-01-23,<ref>{{EzTumblr|http://markrosewater.tumblr.com/post/41287858160/|title=I like your "storm scale" invention so I hope...|2013-01-23}}</ref>},
	{1,2013-10-25,<ref>{{EzTumblr|http://markrosewater.tumblr.com/post/65064665515/|title=Where are dwarves on the storm scale? I know that orcs and dwarves...|2013-10-25}}</ref>},
	{1,2016-06-04,<ref>{{EzTumblr|http://markrosewater.tumblr.com/post/145378547208/|title=Hello Mr. Rosewater! Can you tell us where are dwarves on the Storm Scale?|2016-06-04}}</ref> Reprinted in ''[ [Kaladesh] ]''}, 
	{2,2018-03-20,<ref>{{EzTumblr|http://markrosewater.tumblr.com/post/172073275828/|title=What’s the current outlook for Dwarves in Magic?|2018-03-20}}</ref>}, 
	{3,2018-10-23,<ref>{{EzTumblr|http://markrosewater.tumblr.com/post/17934156953/|title=Where are dwarves on the Beeble Scale?|October 23, 2018}}</ref>}, 
	{2,2019-08-21,<ref name="D&O">{{EzTumblr|https://markrosewater.tumblr.com/post/187167710678/|title=So what are Dwarves and Orcs on the Beeble scale?|August 21, 2019}}</ref>},
	{2,2019-09-30,<ref>{{EzTumblr|https://markrosewater.tumblr.com/post/188050238433/|title=Hi Mark, Since we got some pretty sweet Dwarf cards in Eldraine|September 30, 2019}}</ref>}, 
	{4,2020-03-29,<ref>{{EzTumblr|http://markrosewater.tumblr.com/post/613949342837096448/|title=Judging by my playgroup and another Question Marks...|2020-03-29}}</ref>},
	
	Shows as:     1[5], 1[6], 1[7] Reprinted in Kaladesh, 2[8], 3[9], 2[10], 2[11], 4[12]
]]

-- Little bit of Lua for building tables for the Storm and Rabiah scales.
-- Mechanics have been re-ranked in the past, and it's probably interesting to track their drift.
-- Rather than force editors to track the newest one, let's see if we can't do it in code.

local MaroScaleSandbox = {}

local sub, match, gsub = mw.ustring.sub, mw.ustring.match, mw.ustring.gsub
-- ustring is string library equivalents that work on UTF-8 aka mediawiki text

local MAX_ENTRIES = 500
-- A list of all sets which have been Standard-legal since the introduction of the Storm Scale (September 2012)
local LEGAL_EXPANSIONS = {
	["Return to Ravnica"]		 = "2012-10-05",
	["Gatecrash"] 				 = "2013-02-01",
	["Dragon's Maze"] 			 = "2013-05-03",
	["Magic 2014"] 				 = "2013-06-13",
	["Theros"] 					 = "2013-09-27",
	["Born of the Gods"] 		 = "2014-02-07",
	["Journey into Nyx"]		 = "2014-05-02",
	["Magic 2015"] 				 = "2014-07-18",
	["Khans of Tarkir"] 		 = "2014-09-26",
	["Fate Reforged"] 			 = "2015-01-23",
	["Dragons of Tarkir"] 		 = "2015-03-17",
	["Magic Origins"] 			 = "2015-07-15",
	["Battle for Zendikar"] 	 = "2015-10-02",
	["Oath of the Gatewatch"] 	 = "2016-01-22",
	["Shadows over Innistrad"] 	 = "2016-04-08",
	["Eldritch Moon"] 			 = "2016-06-22",
	["Kaladesh"] 				 = "2016-09-30",
	["Aether Revolt"] 			 = "2016-01-20",
	["Amonkhet"] 				 = "2017-04-28",
	["Hour of Devastation"] 	 = "2017-07-14",
	["Ixalan"]                   = "2017-09-29",
	["Rivals of Ixalan"]         = "2018-01-19",
	["Dominaria"]                = "2018-04-27",
	["Core Set 2019"]            = "2018-07-13",
	["Guilds of Ravnica"]        = "2018-10-05",
	["Ravnica Allegiance"]       = "2019-01-25",
	["War of the Spark"]         = "2019-05-03",
	["Core Set 2020"]            = "2019-07-12",
	["Throne of Eldraine"]       = "2019-10-04",
	["Theros Beyond Death"]      = "2020-01-24",
	["Ikora: Lair of Behemoths"] = "2020-05-15",
	["Core Set 2021"]            = "2020-07-03",
	["Zendikar Rising"]          = "2020-09-25",
	["Kaldheim"]                 = "2021-02-05",
	["Strixhaven"]               = "2021-04-23",
	["Dungeons & Dragons: Adventures in the Forgotten Realms"] = "2021-07-23",
	["Innistrad: Midnight Hunt"] = "2021-09-24",
	["Innistrad: Crimson Vow"]   = "2021-11-19",
	["Kamigawa: Neon Dynasty"]   = "2022-02-18",
	["Streets of New Capenna"]   = "2022-04-29",
	["Dominaria United"]         = "2020-09-09",
	["The Brothers' War"]        = "2020-11-18",
}

-- wikitext for a simple link; displayText is optional as usual
local function generateLink(target, displayText)
	if displayText then
		return "[["..displayText.."|"..target.."]]"
	else
		return "[["..target.."]]"
	end
end

-- parseRatings(raw)
-- PARAM:
--   raw: text for one mechanic's storm scale ratings/entries, in the comma-
--        separated, curly-braced format {rating,date,ref},{...},{...},...
--            rating: integer only
--            date:   must be exactly yyyy-mm-dd format, always hyphen-separated
--            ref:    source, presumably in ref tags; technically could be any text
-- RETURN:
--  (newestRating,      latest rating (number)
--   outputList)        list (table) of lua objects {text,t}
--                          text: wikitext
--                          t:    lua time object with y,m,d
local function parseRatings(raw)
	local outputList = {}
	local newestDate, newestRating = 0, 0
	local rating, refDate, refString, t
	
	-- for each curly-braced entry, extract data and translate to table
	gsub(raw, "{(.-)}", function(a)
		rating, refDate, refString = match(a, "(%d+),([%d%-]+),(.+)")
		rating = tonumber(rating)
		
		-- Dates MUST be yyyy-mm-dd
		t = os.time({year=sub(refDate, 1,4), month=sub(refDate, 6,7), day=sub(refDate, 9,10),})
		-- find most recent entry as we go
		if t > newestDate then
			newestDate = t
			newestRating = rating
		end
		
		-- add to sequence (table) of entries, with ref concatenated on rating
		table.insert(outputList, {text = rating..refString, t = t})
	end)
	
	return newestRating, outputList
end

local function addPrintings(timeline, printings)
	local releaseDate, t
	-- Comma-separated list
	gsub(printings, "([^,]+)", function(set)
		releaseDate = LEGAL_EXPANSIONS[set]
		
		-- The set name provided doesn't match a post-Storm Scale set
		if not releaseDate then return end
		
		-- Dates MUST be yyyy-mm-dd
		t = os.time({year=sub(releaseDate, 1,4), month=sub(releaseDate, 6,7), day=sub(releaseDate, 9,10),})
		
		table.insert(timeline, {text = generateLink(set), t = t})
	end)
	
	return timeline
end

local function sortTimeline(events)
	-- Sort all events into chronological order
	table.sort(events, function(a,b) return a.t < b.t end)
	return events
end

	
function MaroScaleSandbox.TableBuilder(frame)
	local args = frame:getParent().args -- coming from one layer up
	
	-- Imitating navbox, here. I trust they know what they're doing.
	-- Read the arguments in the order they'll be output in, to make references number in the right order.
    local _
    _ = args.type
    _ = args.above
    for i = 1, MAX_ENTRIES do
        _ = args["name" .. tostring(i)]
        _ = args["entry" .. tostring(i)]
        _ = args["note" .. tostring(i)]
        _ = args["ratings" .. tostring(i)]
        _ = args["printings" .. tostring(i)]
    end
	
	-- Seriously this loop is entirely too clever
	local listnums = {}
	for k, v in pairs(args) do
		local listnum = match('' .. k, '^ratings(%d+)$')
		if listnum then table.insert(listnums, {num = tonumber(listnum), alpha = args["entry"..listnum]}) end
	end
	table.sort(listnums, function(a,b) return a.alpha < b.alpha end)

	
	-- Build the table header row
	local type = args.type
	local tbl = mw.html.create("table"):addClass("wikitable"):addClass("sortable")
	tbl:tag("tr")
		:tag("th"):wikitext(type):done()
		:tag("th"):wikitext("Latest ranking"):done()
		:tag("th"):addClass("unsortable"):wikitext("Timeline"):done() -- Last column can't be sorted
	
	local hasNotes = false
	
	-- Reusable loop vars for performance
	local name, entry, note, ratings, printings
	local linkedEntry, lastRating, timeline
	local row, list, cellText
	for i, listnum in ipairs(listnums) do
		listnum = listnum.num
		-- Retrieve the relevant args for this row
		name, entry, note, ratings, printings = args["name"..listnum], args["entry"..listnum], args["note"..listnum], args["ratings"..listnum], args["printings"..listnum]
		
		-- Create a wikilink for the entry, using name as the target article if provided
		linkedEntry = generateLink(entry, name)
		if note then
			if not hasNotes then hasNotes = true end
			linkedEntry = linkedEntry .. frame:expandTemplate({title = "efn", args = { note }})
		end
		
		-- Decode the ratings into a table
		lastRating, timeline = parseRatings(ratings)
		-- Add any printings
		if printings then
			timeline = addPrintings(timeline, printings)
		end
		-- Sort chronologically
		timeline = sortTimeline(timeline)
		
		-- Make the row
		row = tbl:tag("tr")
		
		-- First cell
		row:tag("td"):wikitext(linkedEntry)
		
		-- Second cell
		row:tag("td"):wikitext(lastRating)
			
		-- Third cell
		list = row:tag("td"):tag("ul"):cssText("display:block; margin-top:0; margin-left:0;")
		
		for n, event in pairs(timeline) do
			if n ~= #timeline then
				list:tag("li"):cssText("display:inline-block;"):wikitext(event.text .. ",&nbsp;")
			else
				list:tag("li"):cssText("display:inline-block;"):wikitext(event.text)
			end
		end
	end
	
	if hasNotes then
		return tostring(tbl) .. frame:expandTemplate({title = "notelist"})
	else
		return tbl
	end
end

return MaroScaleSandbox