Module:MaroScaleSandbox
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]
Plane | Latest ranking | Timeline |
---|---|---|
Innistrad | 1 | 1[4] |
Ravnica[d] | 1 | 1[4] |
Mechanic | Latest ranking | Timeline |
---|---|---|
-1/-1 counters | 2 | 1[4], 2[4] |
Grind | 6 | 5[4], 6[4] |
Rhystic | 9 | 9[4] |
Creature type | Latest ranking | Timeline |
---|---|---|
Beeble | 10 | 8[4], 10[4] |
Dauthi | 6 | 6[4] |
Djinn | 4 | 4[2] Reprinted in Khans of Tarkir block and Dominaria. |
Dragon | 1 | 1[1] Iconic |
Elf | 1 | 1[1] Characteristic |
Goblin | 1 | 1[1] Characteristic |
Human | 1 | 1[1] Characteristic |
Hydra | 1 | 1[1] Iconic |
Werewolf | 3 | 3[2] A 6 outside of Innistrad. |
Planeswalker | Latest ranking | Timeline |
---|---|---|
Kiora | 4 | 4[4] |
Nahiri | 4 | 3[4], 4[4] |
Tibalt | 7 | 5[4], 6[4], 7[4] |
Urza | 9 | 9[4] |
Quick links for live pages:
TEST | Latest ranking | Timeline |
---|---|---|
ERROR NO entry2 | 4 | 3[2], 4[2] No entry2! |
Entry is the VISIBLE title | 5 | 5[7] |
Mix ratings and printings (Lorwyn is there but should be ignored) | 8 | 5[4], 6[4], 7[4], 8[4] |
More notes[a] | 4 | 2[6], 4[6] |
- ↑ This is another footnote
Footnotes
- ↑ a b c d e Mark Rosewater (October 22, 2018). "Are the iconic/characteristic races considered a 1 on the Beeble Scale?". Blogatog. Tumblr.
--[[ 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: * (DONE?) learn a bit more how it's used, AND about Modules in general * (DONE) update LEGAL_EXPANSIONS (although it's not used at all rn lol) * 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 ]] --[[ 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. -- Invoked by {{#invoke:MaroScale|TableBuilder}} from the MW template. local MaroScaleSandbox = {} local sub, match, gsub = mw.ustring.sub, mw.ustring.match, mw.ustring.gsub -- ustring equivalents of string library 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", } -- See initLegalExpansions func for how this will be reframed into LEGAL_EXPANSIONS local LEGAL_EXPANSIONS_DATA = { {"2012-10-05", "RTR", "Return to Ravnica"}, {"2013-02-01", "GTC", "Gatecrash"}, {"2013-05-03", "DGM", "Dragon's Maze"}, {"2013-06-13", "M14", "Magic 2014"}, {"2013-09-27", "THS", "Theros"}, {"2014-02-07", "BNG", "Born of the Gods"}, {"2014-05-02", "JOU", "Journey into Nyx"}, {"2014-07-18", "M15", "Magic 2015"}, {"2014-09-26", "KTK", "Khans of Tarkir"}, {"2015-01-23", "FRF", "Fate Reforged"}, {"2015-03-17", "DTK", "Dragons of Tarkir"}, {"2015-07-15", "ORI", "Magic Origins"}, {"2015-10-02", "BFZ", "Battle for Zendikar"}, {"2016-01-22", "OGW", "Oath of the Gatewatch"}, {"2016-04-08", "SOI", "Shadows over Innistrad"}, {"2016-06-22", "EMN", "Eldritch Moon"}, {"2016-09-30", "KLD", "Kaladesh"}, {"2016-01-20", "AER", "Aether Revolt"}, {"2017-04-28", "AKH", "Amonkhet"}, {"2017-07-14", "HOU", "Hour of Devastation"}, {"2017-09-29", "XLN", "Ixalan"}, {"2018-01-19", "RIX", "Rivals of Ixalan"}, {"2018-04-27", "DOM", "Dominaria"}, {"2018-07-13", "M19", "Core Set 2019"}, {"2018-10-05", "GRN", "Guilds of Ravnica"}, {"2019-01-25", "RNA", "Ravnica Allegiance"}, {"2019-05-03", "WAR", "War of the Spark"}, {"2019-07-12", "M20", "Core Set 2020"}, {"2019-10-04", "ELD", "Throne of Eldraine"}, {"2020-01-24", "THB", "Theros Beyond Death"}, {"2020-05-15", "IKO", "Ikora: Lair of Behemoths"}, {"2020-07-03", "M21", "Core Set 2021"}, {"2020-09-25", "ZNR", "Zendikar Rising"}, {"2021-02-05", "KHM", "Kaldheim"}, {"2021-04-23", "STX", "Strixhaven"}, {"2021-07-23", "AFR", "Dungeons & Dragons: Adventures in the Forgotten Realms"}, {"2021-09-24", "MID", "Innistrad: Midnight Hunt"}, {"2021-11-19", "VOW", "Innistrad: Crimson Vow"}, {"2022-02-18", "NEO", "Kamigawa: Neon Dynasty"}, {"2022-04-29", "SNC", "Streets of New Capenna"}, {"2020-09-09", "DMU", "Dominaria United"}, {"2020-11-18", "BRO", "The Brothers' War"}, } -- sets up LEGAL_EXPANSIONS so that all set data can be queried with either -- the set full name or set code, and label the fields local function initLegalExpansions() for _,setData in ipairs(LEGAL_EXPANSIONS_DATA) do --(musing from jerodast - based on my new understanding of Lua, this could -- be done with __index metatable for each setData; doesn't seem worth) setData.releaseDate = setData[1] setData.code = setData[2] setData.name = setData[3] LEGAL_EXPANSIONS[setData[2]] = setData LEGAL_EXPANSIONS[setData[3]] = setData end end -- 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) Parses raw entry text into a sortable sequence (Lua table) and determines most recent rating. 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 (Lua sequence/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 -- Extract data from each curly-braced entry 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 of entries, with ref concatenated on rating table.insert(outputList, {text = rating..refString, t = t}) end) return newestRating, outputList end --[[ addPrintings(timeline, printings) Parses raw list of sets and adds them into date-sortable sequence (Lua table) PARAMS: timeline - existing sequence (Lua table) to add to, each entry with format {text, t}, where text is wikitext and t is a Lua time object w/y,m,d printings - comma separated list of set names in plaintext RETURN: timeline - same format as input parameter, but with new entries containining wikitext for each set, and including wikilinks ]] local function addPrintings(timeline, printings) local setData, releaseDate, t -- Parse each list element delimited by commas gsub(printings, "([^,]+)", function(set) setData = LEGAL_EXPANSIONS[set] mw.logObject(setData) -- The set provided doesn't match a post-Storm Scale set if not setData then return end rd = setData.releaseDate -- Dates MUST be yyyy-mm-dd t = os.time({year=sub(rd, 1,4), month=sub(rd, 6,7), day=sub(rd, 9,10),}) table.insert(timeline, {text = generateLink(setData.code), t = t}) end) return timeline end --[[ sortTimeline(events) Sorts a timeline as generated by parseRatings & addPrintings, by each entry's date PARAM: events - sequence (Lua table); each entry must have a "t" component which is a Lua date object with y,m,d RETURN: sorted events ]] 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 --[[ MaroScaleSandbox.TableBuilder(frame) Entry point for this module. Turns all template arguments into a table as wikitext. See Template:MaroScale for parameter documentation. Note that for any entry###, ratings### is required or the entry will be ignored. ]] function MaroScaleSandbox.TableBuilder(frame) local args = frame:getParent().args -- frame.args: Module/invoke parameters (none expected) -- frame:getParent().args: Template parameters (nameI, entryI, noteI, etc) initLegalExpansions() -- 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. --[[ Commented out by jerodast: I don't understand why this is necessary. Pairs already works on .args and the list will be re-sorted anyway. 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 ]] -- Get parameter numbers for each entry into sequence, sort it by NAME local entryNums = {} for k, v in pairs(args) do -- Extract param numbers based on ratings### parameter (required) local eNum = match('' .. k, '^ratings(%d+)$') if eNum then local entry = args["entry"..eNum] if not entry then entry = "ERROR NO entry"..eNum end table.insert(entryNums, {num = tonumber(eNum), entry = entry}) end end table.sort(entryNums, function(a,b) return a.entry < b.entry end) -- Build the table header row local tbl = mw.html.create("table"):addClass("wikitable"):addClass("sortable") tbl:tag("tr") :tag("th"):wikitext(args.type):done() -- "Mechanic", "Plane", etc :tag("th"):wikitext("Latest ranking"):done() :tag("th"):wikitext("Timeline"):addClass("unsortable"):done() -- Last col 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 each entry identified and sorted by name earlier... for i, eData in ipairs(entryNums) do eNum = eData.num -- original parameter number for this entry entry = eData.entry; -- Retrieve the relevant args for this row name, note = args["name"..eNum], args["note"..eNum] ratings, printings = args["ratings"..eNum], args["printings"..eNum] -- Create a wikilink for the entry, using name as the target article if provided linkedEntry = generateLink(entry, name) if note then hasNotes = true -- Use {{efn}} template (explanatory footnote) linkedEntry = linkedEntry .. frame:expandTemplate({title = "efn", args = { note }}) end -- Decode the ratings into a sequence (Lua 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 -- Each rating is included as an item in an inline HTML list - which may be excessive. --[[ Commented out by jerodast - let's just try regular wikitext. list = row:tag("td"):tag("ul"):cssText("display:block; margin-top:0; margin-left:0;") for j, event in ipairs(timeline) do if j ~= #timeline then list:tag("li"):cssText("display:inline-block;"):wikitext(event.text .. ", ") else list:tag("li"):cssText("display:inline-block;"):wikitext(event.text) end end ]] list = {} for j, event in ipairs(timeline) do table.insert(list, event.text) end row:tag("td"):wikitext(table.concat(list,", ")) end -- Include full footnotes below the table. Note this will include any other -- explanatory footnotes on the page, and if a notelist is included in the -- References section near the bottom page as it usually is, all notes -- from the table will be duplicated there. -- ...I think. (Maybe it breaks completely.) if hasNotes then return tostring(tbl) .. frame:expandTemplate({title = "notelist"}) else return tbl end end return MaroScaleSandbox