Module:Rotten Tomatoes data

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

local p = {}

local months = {'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'}

local aliasesQ = { RottenTomatoes         = "Q105584", RottenTomatoesScore    = "Q108403393", RottenTomatoesAverage  = "Q108403540", Fandango               = "Q5433722", }

local aliasesP = { RottenTomatoesId       = "P1258", reviewScore            = "P444", reviewScoreBy          = "P447", numberOfReviews        = "P7887", pointInTime            = "P585", determinationMethod    = "P459", author                 = "P50", publisher              = "P123", statedIn               = "P248", language               = "P407", retrieved              = "P813", referenceURL           = "P854", archiveURL             = "P1065", title                  = "P1476", formatterURL           = "P1630", archiveDate            = "P2960", }

-- Helper functions local function falsy(x) return x == false or x == nil or x == '' or x == 0 or type(x) == 'table' and next(x) == nil end

-- copied from Module:wd local function parseDate(dateStr, precision) precision = precision or "d"

local i, j, index, ptr local parts = {nil, nil, nil}

if dateStr == nil then return parts[1], parts[2], parts[3] -- year, month, day end

-- 'T' for snak values, '/' for outputs with '/Julian' attached i, j = dateStr:find("[T/]")

if i then dateStr = dateStr:sub(1, i-1) end

local from = 1

if dateStr:sub(1,1) == "-" then -- this is a negative number, look further ahead from = 2 end

index = 1 ptr = 1

i, j = dateStr:find("-", from)

if i then -- year parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10) -- remove '+' sign (explicitly give base 10 to prevent error)

if parts[index] == -0 then parts[index] = tonumber("0") -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead end

if precision == "y" then -- we're done return parts[1], parts[2], parts[3] -- year, month, day end

index = index + 1 ptr = i + 1

i, j = dateStr:find("-", ptr)

if i then -- month parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)

if precision == "m" then -- we're done return parts[1], parts[2], parts[3] -- year, month, day end

index = index + 1 ptr = i + 1 end end

if dateStr:sub(ptr) ~= "" then -- day if we have month, month if we have year, or year parts[index] = tonumber(dateStr:sub(ptr), 10) end

return parts[1], parts[2], parts[3] -- year, month, day end

-- nil dates precede all reasonable dates since year becomes 1 local function datePrecedesDate(aY, aM, aD, bY, bM, bD) aY, aM, aD = aY or 1, aM or 1, aD or 1 bY, bM, bD = bY or 1, bM or 1, bD or 1 if aY < bY then return true end if aY > bY then return false end if aM < bM then return true end if aM > bM then return false end if aD < bD then return true end return false end

-- format options: 'dmy', 'mdy', 'ymd', 'iso' local function format_date(Y, M, D, format) format = format or 'MDY' local s = (D or '') .. (months[M] or '') .. (Y or '') return mw.getCurrentFrame:expandTemplate{title='Date', args={s, format}} end

-- Returns either QID, true, or ErrorString, false local function getentityID(args) local entityID = args.qid if falsy(entityID) then local title = args.title if falsy(title) then local currentID = mw.wikibase.getEntityIdForCurrentPage if currentID then return currentID, true end return Error.error({'No Wikidata item connected to current page. Need qid or title argument.'}), false else -- if not mw.title.makeTitle(0, title).exists then -- 	return Error.error({'Article ' .. title .. ' does not exist.'}), false -- end entityID = mw.wikibase.getEntityIdForTitle(title) if not entityID then return Error.error({'Article "' .. title .. '" does not exist or has no Wikidata item.'}), false end return entityID, true end end --At this point we should have an entityID. Check if valid. if not mw.wikibase.isValidEntityId(entityID) then return Error.error({'Invalid Q-identifier.'}), false end if not mw.wikibase.entityExists(entityID) then return Error.error({'Wikidata item ' .. entityID .. ' does not exist.'}), false end return entityID, true end

local function point_in_time(statement) if not statement.qualifiers then return nil, nil, nil end local pointintime = statement.qualifiers[aliasesP.pointInTime] if pointintime then return parseDate(pointintime[1].datavalue.value.time) end return nil, nil, nil end

local function access_date(statement) if statement.references then local accessdate = statement.references[1].snaks[aliasesP.retrieved] if accessdate then return parseDate(accessdate[1].datavalue.value.time) end end return nil, nil, nil end

local function date_from_statement(statement) local Y, M, D = point_in_time(statement) if Y then return Y, M, D	end Y, M, D = access_date(statement) if Y then return Y, M, D	end if statement.rank == 'preferred' then return 1, 1, 3 elseif statement.rank == 'normal' then return 1, 1, 2 end return 1, 1, 1 end

local function reviewedby_RT(statement) if not statement.qualifiers then return false end local x = statement.qualifiers[aliasesP.reviewScoreBy] return x and x[1].datavalue.value.id == aliasesQ.RottenTomatoes end

local function score_type(statement) local x = nil if statement.qualifiers then x = statement.qualifiers[aliasesP.determinationMethod] end if x then x = x[1].datavalue.value.id	end local y = '' if statement.mainsnak.snaktype == 'value' then y = statement.mainsnak.datavalue.value end if x == aliasesQ.RottenTomatoesScore then return 'percent' elseif x == aliasesQ.RottenTomatoesAverage then return 'average' elseif string.match(y, '^[0-9]%%$') or string.match(y, '^[1-9][0-9]%%$') or string.match(y, '^100%%$') then return 'percent' elseif string.match(y, '^[0-9] percent$') or string.match(y, '^[1-9][0-9] percent$') or string.match(y, '^100 percent$') then return 'percent' elseif string.match(y, '^%d/10$') or string.match(y, '^%d%.%d%d?/10$') then return 'average' elseif string.match(y, '^%d out of 10$') or string.match(y, '^%d%.%d%d? out of 10$') then return 'average' end return nil end

local function most_recent_score_statement(entityID, scoretype) scoretype = scoretype or 'percent' local score_statements = mw.wikibase.getAllStatements(entityID, aliasesP.reviewScore) local newest, nY, nM, nD	for i, v in ipairs(score_statements) do		local Y, M, D = date_from_statement(v) if v.rank ~= 'deprecated' and v.mainsnak.snaktype == 'value' and reviewedby_RT(v) and score_type(v)==scoretype and not datePrecedesDate(Y, M, D, nY, nM, nD) then nY, nM, nD = Y, M, D			newest = v		end end return newest end

local function get_score(entityID, scoretype) scoretype = scoretype or 'percent' local x = most_recent_score_statement(entityID, scoretype) if x == nil then return nil end return x.mainsnak.datavalue.value end

local function get_count(entityID, args) local x = most_recent_score_statement(entityID) if x == nil then return nil end local y = x.qualifiers[aliasesP.numberOfReviews] if y == nil then return nil end local retval = string.match(y[1].datavalue.value.amount, '%d+') -- dont get sign if args ~= nil and args.spell then local s = {[1]=retval} for key, val in pairs(args) do			if key == 1 or key == 'qid' or key == 'title' then

elseif type(key) == 'number' then else s[key] = val end end return mw.getCurrentFrame:expandTemplate{title='Spellnum per MOS', args=s} end return retval end

local function get_rtid(entityID, noprefix) local rtid_statements = mw.wikibase.getBestStatements(entityID, aliasesP.RottenTomatoesId) local newest, nY, nM, nD	for i, v in ipairs(rtid_statements) do		local Y, M, D = date_from_statement(v) if not datePrecedesDate(Y, M, D, nY, nM, nD) then nY, nM, nD = Y, M, D			newest = v		end end if newest == nil then return nil end newest = newest.mainsnak.datavalue.value if noprefix then newest = string.sub(newest, string.find(newest, '/') + 1) end return newest end

local function get_url(entityID) local rtid = get_rtid(entityID) if rtid == nil then return nil end local x = mw.wikibase.getBestStatements(aliasesP.RottenTomatoesId, aliasesP.formatterURL) return (string.gsub(x[1].mainsnak.datavalue.value, '$1', rtid)) end

local function get_date(entityID, part, format) local z = most_recent_score_statement(entityID) if z == nil then return nil end local Y, M, D = date_from_statement(z) if    part == 'year' then return Y or '' elseif part == 'month' then return months[M] or '' elseif part == 'day' then return D or '' end return format_date(Y, M, D, format) end

local function get_access_date(entityID, format) local z = most_recent_score_statement(entityID) if z == nil then return nil end local Y, M, D = access_date(z) if not Y then Y, M, D = point_in_time(z) end return format_date(Y, M, D, format) end

local function get_asof(entityID, args) local s = {} for key, val in pairs(args) do		if key == 1 or key == 'qid' or key == 'title' then elseif key == 2 then s[1] = get_date(entityID, 'year') elseif key == 3 then s[2] = get_date(entityID, 'month') elseif key == 4 then s[3] = get_date(entityID, 'day') elseif type(key) == 'number' then s[key-1] = val else s[key] = val end end return mw.getCurrentFrame:expandTemplate{title='As of', args=s} end

local function get_rtprose(entityID, args) local s = {get_score(entityID), get_score(entityID, 'average'), get_count(entityID)} s[1] = string.match(s[1], '%d+') s[2] = string.match(s[2], '%d%.%d%d?') or string.match(s[2], '%d') for key, val in pairs(args) do		if key == 1 or key == 'qid' or key == 'title' then elseif type(key) == 'number' then s[key + 2] = val else s[key] = val end end return mw.getCurrentFrame:expandTemplate{title='Rotten Tomatoes prose', args=s} end

local function get_edit_icon(entityID) return mw.getCurrentFrame:expandTemplate{title='EditAtWikidata', args={qid=entityID, pid='P444'}} end

local function get_table(entityID) return get_score(entityID) .. ' (' .. get_count(entityID) .. ' reviews)' end

function p.main(frame) local args = getArgs(frame, {		wrappers = 'Template:Rotten Tomatoes data',		removeBlanks = false,	}) return p._main(args) end

function p._main(args) local entityID, is_good = getentityID(args) if not is_good then return entityID -- which is the error message in this case end local command = args[1] if falsy(command) then return Error.error({'Missing command.'}) end command = string.lower(command) local retval if    command == 'score' then retval = get_score(entityID, 'percent') elseif command == 'average' then retval = get_score(entityID, 'average') elseif command == 'count' then retval = get_count(entityID, args) elseif command == 'rtid' then retval = get_rtid(entityID, args.noprefix) elseif command == 'url' then retval = get_url(entityID) elseif command == 'date' then retval = get_date(entityID, 'date', args.df) elseif command == 'year' then retval = get_date(entityID, command) elseif command == 'month' then retval = get_date(entityID, command) elseif command == 'day' then retval = get_date(entityID, command) elseif command == 'access date' or command == 'accessdate' or command == 'access-date' then retval = get_access_date(entityID, args.df) elseif command == 'as of' or command == 'asof' then retval = get_asof(entityID, args) elseif command == 'prose' then retval = get_rtprose(entityID, args) elseif command == 'edit' then retval = get_edit_icon(entityID) elseif command == 'table' then retval = get_table(entityID) else return Error.error({'Invalid command.'}) end if falsy(retval) then return Error.error({'RT data for "' .. command .. '" unavailable.'}) end return retval end

return p