Module:MaroScaleSandbox: Difference between revisions
>Jerodast m (funcs commented) |
>Jerodast (Remainder of comments, and some rephrasings now that I understand Lua better. Major code changes would be renaming a few vars and, what I'm especially curious about, removing the section that pre-loads all expected arguments up to MAX_ENTRIES) |
||
Line 49: | Line 49: | ||
local sub, match, gsub = mw.ustring.sub, mw.ustring.match, mw.ustring.gsub | local sub, match, gsub = mw.ustring.sub, mw.ustring.match, mw.ustring.gsub | ||
-- ustring | -- ustring equivalents of string library that work on UTF-8 aka MediaWiki text | ||
local MAX_ENTRIES = 500 | local MAX_ENTRIES = 500 | ||
Line 98: | Line 98: | ||
} | } | ||
-- | -- Wikitext for a simple link; displayText is optional as usual | ||
local function generateLink(target, displayText) | local function generateLink(target, displayText) | ||
if displayText then | if displayText then | ||
Line 108: | Line 108: | ||
--[[ parseRatings(raw) | --[[ parseRatings(raw) | ||
Parses raw entry text into a sortable Lua | Parses raw entry text into a sortable sequence (Lua table) and determines most | ||
recent rating. | recent rating. | ||
Line 119: | Line 119: | ||
RETURN: | RETURN: | ||
(newestRating, - latest rating (number) | (newestRating, - latest rating (number) | ||
outputList) - list (table) of | outputList) - list (Lua sequence/table) of Lua objects {text,t} | ||
text: wikitext | text: wikitext | ||
t: lua time object with y,m,d | t: lua time object with y,m,d | ||
Line 128: | Line 128: | ||
local rating, refDate, refString, t | local rating, refDate, refString, t | ||
-- | -- Extract data from each curly-braced entry | ||
gsub(raw, "{(.-)}", function(a) | gsub(raw, "{(.-)}", function(a) | ||
rating, refDate, refString = match(a, "(%d+),([%d%-]+),(.+)") | rating, refDate, refString = match(a, "(%d+),([%d%-]+),(.+)") | ||
Line 135: | Line 135: | ||
-- Dates MUST be yyyy-mm-dd | -- Dates MUST be yyyy-mm-dd | ||
t = os.time({year=sub(refDate, 1,4), month=sub(refDate, 6,7), day=sub(refDate, 9,10),}) | 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 | if t > newestDate then | ||
newestDate = t | newestDate = t | ||
Line 141: | Line 141: | ||
end | end | ||
-- | -- Add to sequence of entries, with ref concatenated on rating | ||
table.insert(outputList, {text = rating..refString, t = t}) | table.insert(outputList, {text = rating..refString, t = t}) | ||
end) | end) | ||
Line 149: | Line 149: | ||
--[[ addPrintings(timeline, printings) | --[[ addPrintings(timeline, printings) | ||
Parses raw list of sets and adds them into date-sortable Lua | Parses raw list of sets and adds them into date-sortable sequence (Lua table) | ||
PARAMS: | PARAMS: | ||
timeline - existing Lua | 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 | {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 | printings - comma separated list of set names in plaintext | ||
Line 161: | Line 161: | ||
local function addPrintings(timeline, printings) | local function addPrintings(timeline, printings) | ||
local releaseDate, t | local releaseDate, t | ||
-- | -- Parse each list element delimited by commas | ||
gsub(printings, "([^,]+)", function(set) | gsub(printings, "([^,]+)", function(set) | ||
releaseDate = LEGAL_EXPANSIONS[set] | releaseDate = LEGAL_EXPANSIONS[set] | ||
Line 178: | Line 178: | ||
--[[ sortTimeline(events) | --[[ sortTimeline(events) | ||
Sorts a timeline | Sorts a timeline as generated by parseRatings & addPrintings, by each entry's date | ||
PARAM: events - | PARAM: events - sequence (Lua table); each entry must have a "t" component which | ||
is a Lua date object with y,m,d | is a Lua date object with y,m,d | ||
RETURN: sorted events | RETURN: sorted events | ||
Line 193: | Line 192: | ||
--[[ MaroScaleSandbox.TableBuilder(frame) | --[[ MaroScaleSandbox.TableBuilder(frame) | ||
Entry point for this module. Turns all template arguments | 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) | function MaroScaleSandbox.TableBuilder(frame) | ||
local args = frame:getParent().args -- | local args = frame:getParent().args | ||
-- frame.args: Module/invoke parameters (none expected) | |||
-- frame:getParent().args: Template parameters (nameI, entryI, noteI, etc) | |||
-- Imitating navbox, here. I trust they know what they're doing. | -- 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. | -- 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 _ | local _ | ||
_ = args.type | _ = args.type | ||
Line 212: | Line 217: | ||
_ = args["printings" .. tostring(i)] | _ = args["printings" .. tostring(i)] | ||
end | end | ||
]] | |||
-- | -- Get parameter numbers for each entry into sequence, sort it by NAME | ||
local | local entryNums = {} | ||
for k, v in pairs(args) do | for k, v in pairs(args) do | ||
local | -- Extract param numbers based on ratings### parameter (required) | ||
if | local eNum = match('' .. k, '^ratings(%d+)$') | ||
if eNum then | |||
table.insert(entryNums, {num = tonumber(eNum), entry = args["entry"..eNum]}) | |||
end | |||
end | end | ||
table.sort( | table.sort(entryNums, function(a,b) return a.entry < b.entry end) | ||
-- Build the table header row | -- Build the table header row | ||
local tbl = mw.html.create("table"):addClass("wikitable"):addClass("sortable") | local tbl = mw.html.create("table"):addClass("wikitable"):addClass("sortable") | ||
tbl:tag("tr") | tbl:tag("tr") | ||
:tag("th"):wikitext(type):done() | :tag("th"):wikitext(args.type):done() -- "Mechanic", "Plane", etc | ||
:tag("th"):wikitext("Latest ranking"):done() | :tag("th"):wikitext("Latest ranking"):done() | ||
:tag("th"): | :tag("th"):wikitext("Timeline"):addClass("unsortable"):done() -- Last col can't be sorted | ||
local hasNotes = false | local hasNotes = false | ||
Line 236: | Line 245: | ||
local linkedEntry, lastRating, timeline | local linkedEntry, lastRating, timeline | ||
local row, list, cellText | local row, list, cellText | ||
for i, | |||
-- 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 | -- Retrieve the relevant args for this row | ||
name | 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 | -- Create a wikilink for the entry, using name as the target article if provided | ||
linkedEntry = generateLink(entry, name) | linkedEntry = generateLink(entry, name) | ||
if note then | if note then | ||
hasNotes = true | |||
-- Use {{efn}} template (explanatory footnote) | |||
linkedEntry = linkedEntry .. frame:expandTemplate({title = "efn", args = { note }}) | linkedEntry = linkedEntry .. frame:expandTemplate({title = "efn", args = { note }}) | ||
end | end | ||
-- Decode the ratings into a table | -- Decode the ratings into a sequence (Lua table) | ||
lastRating, timeline = parseRatings(ratings) | lastRating, timeline = parseRatings(ratings) | ||
-- Add any printings | -- Add any printings | ||
Line 267: | Line 281: | ||
-- Third cell | -- Third cell | ||
-- Each rating is included as an item in an inline HTML list - which | |||
-- may be excessive. | |||
list = row:tag("td"):tag("ul"):cssText("display:block; margin-top:0; margin-left:0;") | list = row:tag("td"):tag("ul"):cssText("display:block; margin-top:0; margin-left:0;") | ||
for | for j, event in ipairs(timeline) do | ||
if | if j ~= #timeline then | ||
list:tag("li"):cssText("display:inline-block;"):wikitext(event.text .. ", ") | list:tag("li"):cssText("display:inline-block;"):wikitext(event.text .. ", ") | ||
else | else | ||
Line 278: | Line 294: | ||
end | 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 | if hasNotes then | ||
return tostring(tbl) .. frame:expandTemplate({title = "notelist"}) | return tostring(tbl) .. frame:expandTemplate({title = "notelist"}) |
Revision as of 18:46, 4 July 2022
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 |
|
Ravnica[d] | 1 |
|
Mechanic | Latest ranking | Timeline |
---|---|---|
-1/-1 counters | 2 |
|
Grind | 6 |
|
Rhystic | 9 |
|
Creature type | Latest ranking | Timeline |
---|---|---|
Beeble | 10 |
|
Dauthi | 6 |
|
Djinn | 4 |
|
Dragon | 1 | |
Elf | 1 | |
Goblin | 1 | |
Human | 1 | |
Hydra | 1 | |
Werewolf | 3 |
|
Planeswalker | Latest ranking | Timeline |
---|---|---|
Kiora | 4 |
|
Nahiri | 4 |
|
Tibalt | 7 |
|
Urza | 9 |
|
Quick links for live pages:
Lua error at line 230: attempt to compare nil with string.
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: * 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. -- 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", } -- 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 releaseDate, t -- Parse each list element delimited by commas 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 --[[ 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) -- 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 table.insert(entryNums, {num = tonumber(eNum), entry = args["entry"..eNum]}) 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. 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 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