Permanently protected module
From Wikipedia, the free encyclopedia


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 parts1], parts2], parts3  -- 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

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



        if partsindex == -0 then

            partsindex = 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 parts1], parts2], parts3  -- year, month, day

        end



        index = index + 1

        ptr = i + 1



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



        if i then

            -- month

            partsindex = tonumber(dateStr:sub(ptr, i-1), 10)



            if precision == "m" then

                -- we're done

                return parts1], parts2], parts3  -- 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

        partsindex = tonumber(dateStr:sub(ptr), 10)

    end



    return parts1], parts2], parts3  -- 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 '') .. (monthsM 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.qualifiersaliasesP.pointInTime

	if pointintime then

		return parseDate(pointintime1].datavalue.value.time)

	end

	return nil, nil, nil

end



local function access_date(statement)

	if statement.references then

		local accessdate = statement.references1].snaksaliasesP.retrieved

		if accessdate then

			return parseDate(accessdate1].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.qualifiersaliasesP.reviewScoreBy

	return x and x1].datavalue.value.id == aliasesQ.RottenTomatoes

end



local function score_type(statement)

	local x = nil

	if statement.qualifiers then

		x = statement.qualifiersaliasesP.determinationMethod

	end

	if x then

		x = x1].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.qualifiersaliasesP.numberOfReviews

	if y == nil then

		return nil

	end

	local retval = string.match(y1].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

				skey = 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(x1].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 monthsM 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

			s1 = get_date(entityID, 'year')

		elseif key == 3 then

			s2 = get_date(entityID, 'month')

		elseif key == 4 then

			s3 = get_date(entityID, 'day')

		elseif type(key) == 'number' then

			skey-1 = val

		else

			skey = 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)}

	s1 = string.match(s1], '%d+')

	s2 = string.match(s2], '%d%.%d%d?') or string.match(s2], '%d')

	s"access-date" = get_access_date(entityID, args.df)

	for key, val in pairs(args) do

		if key == 1 or key == 'qid' or key == 'title' then

			

		elseif type(key) == 'number' then

			skey + 2 = val

		else

			skey = 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 = args1

	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