Jump to content

Module:YouTubeSubscribers

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by BrokenSegue (talk | contribs) at 02:04, 18 February 2023 (factor out logic into newestMatching). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

POINT_IN_TIME_PID = "P585"
YT_CHAN_ID_PID= "P2397"
SUB_COUNT_PID = "P8687"

local p = {} 

-- taken from https://en.wikipedia.org/wiki/Module:Wd
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

-- taken from https://en.wikipedia.org/wiki/Module:Wd
local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
	if aY == nil or bY == nil then
		return nil
	end
	aM = aM or 1
	aD = aD or 1
	bM = bM or 1
	bD = 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


function serializeTable(val, name, skipnewlines, depth)
    skipnewlines = skipnewlines or false
    depth = depth or 0

    local tmp = string.rep(" ", depth)

    if name then tmp = tmp .. name .. " = " end

    if type(val) == "table" then
        tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")

        for k, v in pairs(val) do
            tmp =  tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
        end

        tmp = tmp .. string.rep(" ", depth) .. "}"
    elseif type(val) == "number" then
        tmp = tmp .. tostring(val)
    elseif type(val) == "string" then
        tmp = tmp .. string.format("%q", val)
    elseif type(val) == "boolean" then
        tmp = tmp .. (val and "true" or "false")
    else
        tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
    end

    return tmp
end

function getClaimDate(claim)
	if claim['qualifiers'] and claim['qualifiers'][POINT_IN_TIME_PID] then 
		local pointsInTime = claim['qualifiers'][POINT_IN_TIME_PID]
		if #pointsInTime ~= 1 then
			-- be conservative in what we accept
			return nil
		end
		local pointInTime = pointsInTime[1]
		if pointInTime and pointInTime['datavalue'] and pointInTime['datavalue']['value'] and pointInTime['datavalue']['value']['time'] then
			return parseDate(pointInTime['datavalue']['value']['time'])
		end
		
	end
	return nil
end

-- for a given property and qualifier pair returns the newest statement that matches
function newestMatching(e, prop, qual, targetQualValue)
	local statements = e:getBestStatements(prop)
	local newestStatement = nil
	local newestStatementYr = nil
	local newestStatementMo = nil
	local newestStatementDay = nil
    for k, v in pairs(statements) do
    	if v['qualifiers'] and v['qualifiers'][qual] then
    		local quals = v['qualifiers'][qual]
    		-- should only have one instance of the qualifier on a statement
    		if #quals == 1 then
    			local qual = quals[1]
    			if qual['datavalue'] and qual['datavalue']['value'] then
    				local qualValue = qual['datavalue']['value']
    				if qualValue == targetQualValue then
	    				local targetYr, targetMo, targetDay = getClaimDate(v)
	    				if targetYr then
	    					local older = datePrecedesDate(targetYr, targetMo, targetDay, newestStatementYr, newestStatementMo, newestStatementDay)
		    				mw.logObject(qual)

	    					if older == nil or not older then
	    							    				mw.logObject(v)

	    						newestStatementYr, newestStatementMo, newestStatementDay = targetYr, targetMo, targetDay
	    						newestStatement = v
	    					end
	    				end
    				end
    			end
    		end
    	end
	end
	if newestStatement then
		return newestStatement
	end
	return nil
end

function getEntity ( frame )
	local qid = nil
	if frame.args then
		qid = frame.args["qid"]
	end
	if not qid then
		qid = mw.wikibase.getEntityIdForCurrentPage()
	end
	if not qid then
		error("No qid found for page")
	end
	local e = mw.wikibase.getEntity(qid)
	if not e then
		error("No such item found: " .. qid)
	end
	return e
end

-- find the channel ID we are going to be getting the sub counts for
function getBestYtChanId(e) 
	local chanIds = e:getBestStatements(YT_CHAN_ID_PID)
	if #chanIds == 1 then
		local chan = chanIds[1]
		if chan and chan["mainsnak"] and chan["mainsnak"]["datavalue"] and chan["mainsnak"]["datavalue"]["value"] then
			return chan["mainsnak"]["datavalue"]["value"]
		end
	end
	return nil
end

function p.date( frame )
	local e = getEntity(frame)
	local chanId = getBestYtChanId(e)
	
	local subCounts = e:getBestStatements(SUB_COUNT_PID)
	local subCount = nil
    for k, v in pairs(subCounts) do
    	if v['qualifiers'] and v['qualifiers'][YT_CHAN_ID_PID] then 
    		local yt_qualifier = v['qualifiers'][YT_CHAN_ID_PID]
    		local yt_year = nil
    		local yt_month = nil
    		local yt_day = nil
    		yt_year, yt_month, yt_day = getClaimDate(v)
    		if not yt_year then
    			return nil
    		end
    		local dateString = yt_year .. "|"
    		-- construct YYYY|mm|dd date string
    		if yt_month then
    			dateString = dateString .. yt_month .. "|"
    			if yt_day then
    				dateString = dateString .. yt_day
    			end
    		end
    		return "{{Format date|" ..dateString .. "}}"
    	end
    end
end

function p.subCount( frame )
	local e = getEntity(frame)
	local subCount = nil
	local chanId = getBestYtChanId(e)
	if chanId then
		local bestStatement = newestMatching(e, SUB_COUNT_PID, YT_CHAN_ID_PID, chanId)
		mw.logObject(bestStatement)
		local subCounts = e:getBestStatements(SUB_COUNT_PID)
	    for k, v in pairs(subCounts) do
	    	if v['qualifiers'] and v['qualifiers'][YT_CHAN_ID_PID] then 
	    		local yt_qualifier = v['qualifiers'][YT_CHAN_ID_PID]
	    		if yt_qualifier[1]['datavalue']['value'] == chanId then
	    			subCount = v["mainsnak"]["datavalue"]["value"]["amount"]
	    		end
	    	end
	    end
	end
    if subCount then
    	return tonumber(subCount)
    else
    	error("could not find sub count")
    	return "?"
    end
end

return p