Module:YouTubeSubscribers: Difference between revisions

Content deleted Content added
No edit summary
m Improve readability of long, compound if-statements
 
(59 intermediate revisions by 4 users not shown)
Line 1:
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 serializeTable(val, name, skipnewlines, depth)
function parseDate(dateStr, precision)
skipnewlines = skipnewlines or false
depth precision = depthprecision or 0"d"
 
local tmpi, = string.rep("j, "index, depth)ptr
local parts = {nil, nil, nil}
 
if dateStr == nil then
if name then tmp = tmp .. name .. " = " end
return parts[1], parts[2], parts[3] -- year, month, day
end
 
-- 'T' for snak values, '/' for outputs with '/Julian' attached
if type(val) == "table" then
i, j = dateStr:find("[T/]")
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
 
if i then
for k, v in pairs(val) do
dateStr = dateStr:sub(1, i-1)
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
 
local from = 1
tmp = tmp .. string.rep(" ", depth) .. "}"
 
elseif type(val) == "number" then
if dateStr:sub(1,1) == "-" then
tmp = tmp .. tostring(val)
-- this is a negative number, look further ahead
elseif type(val) == "string" then
from = 2
tmp = tmp .. string.format("%q", val)
end
elseif type(val) == "boolean" then
 
tmp = tmp .. (val and "true" or "false")
index = 1
else
ptr = 1
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
 
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
elseif aY > bY then
return false
elseif aM < bM then
return true
elseif aM > bM then
return false
elseif aD < bD then
return true
end
 
return false
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
error("Encountered a statement with zero or multiple point in time (P85) qualifiers. Please add or remove point in time information so each statement has exactly one")
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 list of statements find the newest one with a matching qual
function newestMatchingStatement(statements, qual, targetQualValue)
local newestStatement = nil
local newestStatementYr = nil
local newestStatementMo = nil
local newestStatementDay = nil
for k, v in pairs(statements) do
if v['rank'] ~= "deprecated" and 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)
if older == nil or not older then
newestStatementYr, newestStatementMo, newestStatementDay = targetYr, targetMo, targetDay
newestStatement = v
end
end
end
end
end
end
end
return newestStatement
end
 
-- for a given property and qualifier pair returns the newest statement that matches
return tmp
function newestMatching(e, prop, qual, targetQualValue)
-- first check the best statements
local statements = e:getBestStatements(prop)
local newestStatement = newestMatchingStatement(statements, qual, targetQualValue)
if newestStatement then
return newestStatement
end
-- try again with all statements if nothing so far
statements = e:getAllStatements(prop)
newestStatement = newestMatchingStatement(statements, qual, targetQualValue)
if newestStatement then
return newestStatement
end
return nil
end
 
function p.subCountgetEntity ( frame )
local qid = nil
if frame.args["qid"] then
qid = frame.args["qid"]
end
if not qid then
qid = mw.wikibase.getEntityIdForCurrentPage()
end
if not qid then
local e = nil
return e
end
local e = mw.wikibase.getEntity(qid)
assert(e, "No such item found: " .. qid)
local chanIds = e:getBestStatements("P2397")
return e
local firstChanId = chanIds[1]["mainsnak"]["datavalue"]["value"]
end
 
local subCounts = e:getBestStatements("P8687")
-- find the channel ID we are going to be getting the sub counts for
local subCount = nil
function getBestYtChanId(e)
for k, v in pairs(subCounts) do
local chanIds = e:getBestStatements(YT_CHAN_ID_PID)
if v['qualifiers'] and v['qualifiers']['P2397'] then
if #chanIds == 1 then
local yt_qualifier = v['qualifiers']['P2397']
local chan = chanIds[1]
--if yt_qualifier['datavalue']['value'] == firstChanId then
if chan and
subCount = serializeTable(yt_qualifier) .. serializeTable(v)
chan["mainsnak"] and
--end
chan["mainsnak"]["datavalue"] and
end
chan["mainsnak"]["datavalue"]["value"]
then
return chan["mainsnak"]["datavalue"]["value"]
end
end
return nil
end
 
function returnError(frame, eMessage)
return frame:expandTemplate{ title = 'error', args = { eMessage } } .. "[[Category:Pages with YouTubeSubscribers module errors]]"
end
 
-- the date of the current YT subscriber count
return "hello 6 :" .. subCount
function p.date( frame )
local e = getEntity(frame)
assert(e, "No qid found for page. Please make a Wikidata item for this article")
local chanId = getBestYtChanId(e)
assert(chanId, "Could not find a single best YouTube channel ID for this item. Add a YouTube channel ID or set the rank of one channel ID to be preferred")
local s = newestMatching(e, SUB_COUNT_PID, YT_CHAN_ID_PID, chanId)
if s then
local yt_year, yt_month, yt_day = getClaimDate(s)
if not yt_year then
return nil
end
local dateString = yt_year .. "|"
-- construct YYYY|mm|dd date string
if yt_month and yt_month ~= 0 then
dateString = dateString .. yt_month .. "|"
-- truncate the day of month
--if yt_day and yt_day ~= 0 then
-- dateString = dateString .. yt_day
--end
end
return frame:expandTemplate{title="Format date", args = {yt_year, yt_month, yd_day}}
end
error("Could not find a date for YouTube subscriber information. Is there a social media followers statement (P8687) qualified with good values for P585 and P2397?")
end
 
function p.dateNice( frame )
local status, obj = pcall(p.date, frame)
if status then
return obj
else
return returnError(frame, obj)
end
end
 
-- the most up to date number of subscribers
function p.subCount( frame )
local subCount = nil
local e = getEntity(frame)
if not e then
subCount = -424
return tonumber(subCount)
end
local chanId = getBestYtChanId(e)
if chanId then
local s = newestMatching(e, SUB_COUNT_PID, YT_CHAN_ID_PID, chanId)
if s and
s["mainsnak"] and
s['mainsnak']["datavalue"] and
s['mainsnak']["datavalue"]["value"] and
s['mainsnak']["datavalue"]['value']['amount']
then
subCount = s['mainsnak']["datavalue"]['value']['amount']
end
else
subCount = -404
end
if subCount then
return tonumber(subCount)
else
subCount = -412
return tonumber(subCount)
end
end
 
function p.subCountNice( frame )
local status, obj = pcall(p.subCount, frame)
if status then
if obj >= 0 then
return frame:expandTemplate{title="Format price", args = {obj}}
else
return obj
end
else
return returnError(frame, obj)
end
end