وحدة:WdItem
المظهر
-- Module: WdItem
-- Object-oriented Wikidata access module for Lua
-- Refactored from WikidataIB for direct Lua usage
require("strict")
local p = {}
-- i18n configuration
local i18n = {
["errors"] = {
["property-not-found"] = "الخاصية غير موجودة.",
["entity-not-found"] = "الكيان على ويكي بيانات غير موجود.",
["unknown-claim-type"] = "نوع الادعاء غير معروف.",
["local-article-not-found"] = "المعرف متاح على ويكي بيانات، ولكن المقال غير متاح على ويكيبيديا",
},
["months"] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
},
["Unknown"] = "Unknown",
["NaN"] = "Not a number",
["missinginfocat"] = "[[تصنيف:Articles with missing Wikidata information]]",
["list separator"] = ", ",
["list separator-ar"] = "، ",
}
-- Date precision constants
local dp = {
[6] = "millennium",
[7] = "century",
[8] = "decade",
[9] = "year",
[10] = "month",
[11] = "day",
}
local options_aliases = {
["osd"] = "onlysourced",
["wdlinks"] = "wdl",
["uabbr"] = "unitabbr",
["qo"] = "qualsonly",
["lp"] = "linkprefix",
["lprefix"] = "linkprefix",
["qlp"] = "qlinkprefix",
["sn"] = "shortname",
["uselabel"] = "uselbl",
["plaindate"] = "pd",
["qualifierdateformat"] = "qdf",
["df"] = "qdf",
["qshortname"] = "qsn"
}
setmetatable(options_aliases, { __index = function(t, k) return k end })
-------------------------------------------------------------------------------
-- Internal helper functions
-------------------------------------------------------------------------------
local function findLang(langcode)
local langobj
langcode = mw.text.trim(langcode or "")
if mw.language.isKnownLanguageTag(langcode) then
langobj = mw.language.new(langcode)
else
langcode = mw.getCurrentFrame():callParserFunction('int', {'lang'})
if mw.language.isKnownLanguageTag(langcode) then
langobj = mw.language.new(langcode)
else
langobj = mw.language.getContentLanguage()
end
end
return langobj
end
local function parseParam(param, default)
if param == nil then
return default
end
return param
end
local function setRanks(rank)
rank = (rank or ""):lower()
local ranks = {}
for w in string.gmatch(rank, "%a+") do
w = w:sub(1,1)
if w == "b" or w == "p" or w == "n" or w == "d" then
ranks[w] = true
end
end
if ranks.b or not next(ranks) then
ranks.p = true
ranks.n = true
end
return ranks
end
local function labelOrId(id, lang)
if lang == "default" then lang = findLang().code end
local label
if lang then
label = mw.wikibase.getLabelByLang(id, lang)
end
if not label then
local trylang = {"ar","en","fr"}
local triedlang = lang
for _,lang in pairs(trylang) do
if triedlang ~= lang then
label = mw.wikibase.getLabelByLang(id, lang)
if label ~= "" then
break
end
end
end
end
if label then
return mw.text.nowiki(label), true
else
return id, false
end
end
local function roundto(x, sf)
if x == 0 then return 0 end
local s = 1
if x < 0 then
x = -x
s = -1
end
if sf < 1 then sf = 1 end
local p = 10 ^ (math.floor(math.log10(x)) - sf + 1)
x = math.floor(x / p + 0.5) * p * s
if x == math.floor(x) then x = math.floor(x) end
return x
end
local function decimalPrecision(x, p)
local s = 1
if x < 0 then
x = -x
s = -1
end
if not tonumber(p) then p = 1e-4
elseif p > 1 then p = 1
elseif p < 1e-6 then p = 1e-6
else p = 10 ^ math.floor(math.log10(p))
end
x = math.floor(x / p + 0.5) * p * s
if x == math.floor(x) then x = math.floor(x) end
if math.abs(x) < 1e-4 then x = string.format("%f", x) end
return x
end
local function decimalToDMS(x, p)
if not tonumber(p) then p = 1e-4 end
local d = math.floor(x)
local ms = (x - d) * 60
if p > 0.5 then
if ms > 30 then d = d + 1 end
ms = 0
end
local m = math.floor(ms)
local s = (ms - m) * 60
if p > 0.008 then
if s > 30 then m = m +1 end
s = 0
elseif p > 0.00014 then
s = math.floor(s + 0.5)
elseif p > 0.000014 then
s = math.floor(10 * s + 0.5) / 10
elseif p > 0.0000014 then
s = math.floor(100 * s + 0.5) / 100
else
s = math.floor(1000 * s + 0.5) / 1000
end
return d, m, s
end
local function dateFormat(timestamp, dprec, df, bcf, pd, qualifiers, lang, adj, model)
df = (df or ""):lower()
if df == "ymd" then
if timestamp:sub(1,1) == "+" then
return timestamp:sub(2,11)
else
local yr = tonumber(timestamp:sub(2,5)) - 1
yr = ("000" .. yr):sub(-4)
if yr ~= "0000" then yr = "-" .. yr end
return yr .. timestamp:sub(6,11)
end
end
timestamp = timestamp:gsub("%-00%-00T", "-01-01T")
dprec = dprec or 11
if df == "y" and dprec > 9 then dprec = 9 end
dprec = dprec>11 and 11 or dprec
dprec = dprec<6 and 6 or dprec
bcf = (bcf or ""):upper()
pd = (pd or ""):sub(1,1):lower()
if pd == "" or pd == "n" or pd == "f" or pd == "0" then pd = false end
lang = lang or findLang().code
adj = adj or ""
local bc = timestamp:sub(1, 1)=="-" and "BC" or ""
local year, month, day = timestamp:match("[+-](%d*)-(%d*)-(%d*)T")
local iso = tonumber(year)
if dprec == 6 then iso = math.floor( (iso - 1) / 1000 ) + 1 end
if dprec == 7 then iso = math.floor( (iso - 1) / 100 ) + 1 end
if dprec == 8 then iso = math.floor( iso / 10 ) .. "0" end
if dprec == 10 then iso = year .. "-" .. month end
if dprec == 11 then iso = year .. "-" .. month .. "-" .. day end
local sc = not pd and qualifiers and qualifiers.P1480
if sc then
for k1, v1 in pairs(sc) do
if v1.datavalue and v1.datavalue.value.id == "Q5727902" then
adj = "circa"
break
end
end
end
local calendarmodel = ""
if tonumber(year) > 1582 and dprec > 8 and not pd and model == "http://www.wikidata.org/entity/Q1985786" then
calendarmodel = "julian"
end
-- Simplified date formatting (complex date module not available)
local fdate = tostring(iso)
if bc ~= "" then
fdate = fdate .. " " .. (bcf == "BC" and "BC" or "BCE")
end
if adj == "circa" then
fdate = "c. " .. fdate
end
return fdate
end
local function sourced(claim)
if claim.references then
for kr, vr in pairs(claim.references) do
local ref = mw.wikibase.renderSnaks(vr.snaks)
if not ref:find("Wiki") then
return true
end
end
end
end
local function linkedItem(id, args)
local lprefix = (args.linkprefix or ""):gsub('"', '')
local lpostfix = (args.lpostfix or ""):gsub('"', '')
local prefix = (args.prefix or ""):gsub('"', '')
local postfix = (args.postfix or ""):gsub('"', '')
local dtxt = args.dtxt
local lang = args.lang or "ar"
local uselbl = parseParam( args.uselbl, true)
local disp
local sitelink = mw.wikibase.getSitelink(id)
local label, islabel
if dtxt then
label, islabel = dtxt, true
else
label, islabel = labelOrId(id, args.lang)
end
if sitelink then
if not uselbl then
local pos = sitelink:find(":") or 0
local slink = sitelink
if pos > 0 then
local pfx = sitelink:sub(1,pos-1)
if mw.site.namespaces[pfx] then
slink = sitelink:sub(pos+1)
end
end
slink = slink:gsub("%s%(.+%)$", ""):gsub(",.+$", "")
if label:find("^%u") then
label = slink:gsub("^(%l)", string.upper)
else
label = slink:gsub("^(%u)", string.lower)
end
end
disp = "[[" .. lprefix .. sitelink .. lpostfix .. "|" .. prefix .. label .. postfix .. "]]"
elseif islabel then
disp = prefix .. label .. postfix
else
disp = prefix .. label .. postfix .. i18n.missinginfocat
end
return disp
end
local function rendersnak(propval, args, linked, lpre, lpost, pre, post, uabbr, filter)
lpre = lpre or ""
lpost = lpost or ""
pre = pre or ""
post = post or ""
args.lang = args.lang or findLang().code
local snak = propval.mainsnak or propval
local dtype = snak.datatype
local dv = snak.datavalue
dv = dv and dv.value
local val, mlt
if propval.rank and not args.reqranks[propval.rank:sub(1, 1)] then
return nil
elseif snak.snaktype == "somevalue" then
val = i18n["Unknown"]
elseif snak.snaktype == "novalue" then
-- return nothing
elseif dtype == "wikibase-item" then
local qnumber = dv.id
if linked then
val = linkedItem(qnumber, args)
else
local label, islabel = labelOrId(qnumber, args.lang)
val = pre .. label .. post
end
elseif dtype == "time" then
val = dateFormat(dv.time, dv.precision, args.df, args.bc, args.pd, propval.qualifiers, args.lang, "", dv.calendarmodel)
elseif dtype == "commonsMedia" or dtype == "external-id" or dtype == "string" or dtype == "url" then
if (lpre == "" or lpre == ":") and lpost == "" then
val = pre .. dv .. post
elseif dtype == "external-id" then
val = "[" .. lpre .. dv .. lpost .. " " .. pre .. dv .. post .. "]"
else
val = "[[" .. lpre .. dv .. lpost .. "|" .. pre .. dv .. post .. "]]"
end
elseif dtype == "quantity" then
local amount = tonumber(dv.amount) or i18n["NaN"]
local unit = ""
local unitqid = string.match(dv.unit, "(Q%d+)")
if filter and unitqid ~= filter then return nil end
if unitqid then
local uname = mw.wikibase.getLabelByLang(unitqid, args.lang) or ""
if uname ~= "" then unit = " " .. uname end
end
val = tostring(amount) .. unit
elseif dtype == "globe-coordinate" then
local lat, long, prec = dv.latitude, dv.longitude, dv.precision
local show = (args.show or ""):lower()
if show == "lat" then
val = decimalPrecision(lat, prec)
elseif show == "lon" then
val = decimalPrecision(long, prec)
elseif show == "longlat" then
val = decimalPrecision(long, prec) .. ", " .. decimalPrecision(lat, prec)
else
local ns = lat < 0 and "S" or "N"
local ew = long < 0 and "W" or "E"
if lat < 0 then lat = -lat end
if long < 0 then long = -long end
val = decimalPrecision(lat, prec) .. "°" .. ns .. " " .. decimalPrecision(long, prec) .. "°" .. ew
end
elseif dtype == "monolingualtext" then
val = pre .. dv.text .. post
mlt = dv.language
else
val = "unknown data type: " .. dtype
end
return val, mlt
end
local function assembleoutput(out, args, entityID, propertyID)
local sorted = parseParam(args.sorted, false)
local noic = parseParam(args.noicon, false)
local list = args.list or ""
local separator = args.sep or ""
separator = string.gsub(separator, '"', '')
if separator == "" then
local rtl_lang = {ar = true, fa = true, ur = true, ku = true, he = true}
separator = rtl_lang[args.lang] and i18n["list separator-ar"] or i18n["list separator"]
end
local strout
if #out > 0 then
if sorted then table.sort(out) end
if list == "" then
strout = table.concat(out, separator)
elseif list:lower() == "prose" then
strout = mw.text.listToText(out)
else
strout = mw.getCurrentFrame():expandTemplate{title = list, args = out}
end
else
strout = nil
end
return strout
end
local function propertyvalueandquals(objproperty, args, qualID)
-- needs this style of declaration because it's re-entrant
-- onlysourced is a boolean passed to return only values sourced to other than Wikipedia
-- if nothing or an empty string is passed set it true
local onlysrc = parseParam(args.onlysourced, false)
-- linked is a a boolean that enables the link to a local page via sitelink
-- if nothing or an empty string is passed set it true
local linked = parseParam(args.linked, true)
-- prefix is a string that may be nil, empty (""), or a string of characters
-- this is prefixed to each value
-- useful when when multiple values are returned
-- any double-quotes " are stripped out, so that spaces may be passed
local prefix = (args.prefix or ""):gsub('"', '')
-- postfix is a string that may be nil, empty (""), or a string of characters
-- this is postfixed to each value
-- useful when when multiple values are returned
-- any double-quotes " are stripped out, so that spaces may be passed
local postfix = (args.postfix or ""):gsub('"', '')
-- linkprefix is a string that may be nil, empty (""), or a string of characters
-- this creates a link and is then prefixed to each value
-- useful when when multiple values are returned and indirect links are needed
-- any double-quotes " are stripped out, so that spaces may be passed
local lprefix = (args.linkprefix or ""):gsub('"', '')
-- linkpostfix is a string that may be nil, empty (""), or a string of characters
-- this is postfixed to each value when linking is enabled with lprefix
-- useful when when multiple values are returned
-- any double-quotes " are stripped out, so that spaces may be passed
local lpostfix = (args.linkpostfix or ""):gsub('"', '')
-- wdlinks is a boolean passed to enable links to Wikidata when no article exists
-- if nothing or an empty string is passed set it false
local wdl = parseParam(args.wdl, false)
-- unitabbr is a boolean passed to enable unit abbreviations for common units
-- if nothing or an empty string is passed set it false
local uabbr = parseParam(args.unitabbr, false)
-- qualsonly is a boolean passed to return just the qualifiers
-- if nothing or an empty string is passed set it false
local qualsonly = parseParam(args.qualsonly, false)
-- maxvals is a string that may be nil, empty (""), or a number
-- this determines how many items may be returned when multiple values are available
-- setting it = 1 is useful where the returned string is used within another call, e.g. image
local maxvals = tonumber(args.maxvals) or 0
-- pd (plain date) is a string: yes/true/1 | no/false/0 | adj
-- to disable/enable "sourcing cirumstances" or use adjectival form for the plain date
local pd = args.pd or "no"
args.pd = pd
-- allow qualifiers to have a different date format; default to year unless qualsonly is set
args.qdf = args.qdf or (not qualsonly and "y")
local lang = args.lang or (args.langs and args.langs[1]) or findLang().code
-- qualID is a string list of wanted qualifiers or "ALL"
qualID = qualID or ""
-- capitalise list of wanted qualifiers and substitute "DATES"
qualID = qualID:upper():gsub("DATES", "P580, P582")
local allflag = (qualID == "ALL")
-- create table of wanted qualifiers as key
local qwanted = {}
-- create sequence of wanted qualifiers
local qorder = {}
for q in mw.text.gsplit(qualID, "%p") do -- split at punctuation and iterate
local qtrim = mw.text.trim(q)
if qtrim ~= "" then
qwanted[mw.text.trim(q)] = true
qorder[#qorder+1] = qtrim
end
end
-- qsep is the output separator for rendering qualifier list
local qsep = (args.qsep or ""):gsub('"', '')
-- qargs are the arguments to supply to assembleoutput()
local qargs = {
["osd"] = "false",
["linked"] = tostring(linked),
["prefix"] = args.qprefix,
["postfix"] = args.qpostfix,
["linkprefix"] = args.qlinkprefix,
["linkpostfix"] = args.qlinkpostfix,
["wdl"] = "false",
["unitabbr"] = tostring(uabbr),
["maxvals"] = 0,
["sorted"] = tostring(args.qsorted),
["noicon"] = "true",
["list"] = args.qlist,
["sep"] = qsep,
-- ["langobj"] = args.langobj,
["lang"] = args.lang or (args.langs and args.lang[1]),
["df"] = args.qdf,
["sn"] = parseParam(args.qsn, false),
}
-- all proper values of a Wikidata property will be the same type as the first
-- qualifiers don't have a mainsnak, properties do
--#Modified line
local datatype = objproperty[1].datatype or (objproperty[1].mainsnak and objproperty[1].mainsnak.datatype)
-- out[] holds the a list of returned values for this property
-- mlt[] holds the language code if the datatype is monolingual text
local out = {}
local mlt = {}
for k, v in ipairs(objproperty) do
local hasvalue = true
if (onlysrc and not sourced(v)) then
-- no value: it isn't sourced when onlysourced=true
hasvalue = false
else
local val, lcode = rendersnak(v, args, linked, lprefix, lpostfix, prefix, postfix, uabbr)
if not val then
hasvalue = false -- rank doesn't match
elseif qualsonly and qualID then
-- suppress value returned: only qualifiers are requested
else
out[#out+1], mlt[#out+1] = val, lcode
end
end
-- See if qualifiers are to be returned:
local snak = v.mainsnak or v
if hasvalue and v.qualifiers and qualID ~= "" and snak.snaktype~="novalue" then
-- collect all wanted qualifier values returned in qlist, indexed by propertyID
local qlist = {}
local timestart, timeend = "", ""
-- loop through qualifiers
for k1, v1 in pairs(v.qualifiers) do
if allflag or qwanted[k1] then
if k1 == "P1326" then
local ts = v1[1].datavalue.value.time
local dp = v1[1].datavalue.value.precision
qlist[k1] = dateFormat(ts, dp, args.qdf, args.bc, pd, "", lang, "before")
elseif k1 == "P1319" then
local ts = v1[1].datavalue.value.time
local dp = v1[1].datavalue.value.precision
qlist[k1] = dateFormat(ts, dp, args.qdf, args.bc, pd, "", lang, "after")
elseif k1 == "P580" then
timestart = propertyvalueandquals(v1, qargs)[1] or "" -- treat only one start time as valid
elseif k1 == "P582" then
timeend = propertyvalueandquals(v1, qargs)[1] or "" -- treat only one end time as valid
else
local q = assembleoutput(propertyvalueandquals(v1, qargs), qargs)
-- we already deal with circa via 'sourcing circumstances' if the datatype was time
-- circa may be either linked or unlinked *** internationalise later ***
if datatype ~= "time" or q ~= "circa" and not (type(q) == "string" and q:find("circa]]")) then
qlist[k1] = q
end
end
end -- of test for wanted
end -- of loop through qualifiers
-- set date separator
local t = timestart .. timeend
-- *** internationalise date separators later ***
local dsep = "–"
if t:find("%s") or t:find(" ") then dsep = " – " end
-- set the order for the list of qualifiers returned; start time and end time go last
if next(qlist) then
local qlistout = {}
if allflag then
for k2, v2 in pairs(qlist) do
qlistout[#qlistout+1] = v2
end
else
for i2, v2 in ipairs(qorder) do
qlistout[#qlistout+1] = qlist[v2]
end
end
if t ~= "" then
qlistout[#qlistout+1] = timestart .. dsep .. timeend
end
local qstr = assembleoutput(qlistout, qargs)
if qualsonly then
out[#out+1] = qstr
else
out[#out] = out[#out] .. " (" .. qstr .. ")"
end
elseif t ~= "" then
if qualsonly then
if timestart == "" then
out[#out+1] = timeend
elseif timeend == "" then
out[#out+1] = timestart
else
out[#out+1] = timestart .. dsep .. timeend
end
else
out[#out] = out[#out] .. " (" .. timestart .. dsep .. timeend .. ")"
end
end
end -- of test for qualifiers wanted
if maxvals > 0 and #out >= maxvals then break end
end -- of for each value loop
-- we need to pick one value to return if the datatype was "monolingualtext"
-- if there's only one value, use that
-- otherwise look through the fallback languages for a match
if datatype == "monolingualtext" and #out >1 then
lang = mw.text.split( lang, '-', true )[1]
local fbtbl = mw.language.getFallbacksFor( lang )
table.insert( fbtbl, 1, lang )
local bestval = ""
local found = false
for idx1, lang1 in ipairs(fbtbl) do
for idx2, lang2 in ipairs(mlt) do
if (lang1 == lang2) and not found then
bestval = out[idx2]
found = true
break
end
end -- loop through values of property
end -- loop through fallback languages
if found then
-- replace output table with a table containing the best value
out = { bestval }
else
-- more than one value and none of them on the list of fallback languages
-- sod it, just give them the first one
out = { out[1] }
end
end
return out
end
-------------------------------------------------------------------------------
-- WdItem Class Definition
-------------------------------------------------------------------------------
local function mergeOptions(defaults, overrides)
overrides = overrides or {}
local result = {}
for k, v in pairs(defaults) do
result[k] = v
end
for k, v in pairs(overrides) do
result[k] = v
end
return result
end
local WdItem = {}
WdItem.__index = WdItem
function WdItem:__tostring()
return "WdItem: " .. (self.qid or "no QID")
end
function WdItem:UpdateOptions(newOpts)
for k, v in pairs(newOpts or {}) do
local key = options_aliases[k]
self.options[key] = v
-- Set default options
if key == 'langs' and #v > 0 then
self.options.langobj = findLang(self.options.langs[1])
self.options.lang = self.options.langobj.code
if not self.options.langobj then
self.options.langobj = findLang(self.options.lang)
self.options.lang = self.options.langobj.code
end
end
if key == 'rank' then
self.options.reqranks = setRanks(self.options.rank)
end
end
if not self.options.reqranks then
self.options.reqranks = setRanks('best')
end
end
function WdItem.new(qid, options)
local self = setmetatable({}, WdItem)
if qid == "" then qid = nil end
if (not qid) and mw.wikibase then
qid = mw.wikibase.getEntityIdForCurrentPage()
end
self.qid = qid
self.snak = nil
self.options = options or {}
self:UpdateOptions(options)
return self
end
function WdItem.newFromSnaks(snakTable, options)
local self = setmetatable({}, WdItem)
self.qid = nil
self.snak = snakTable or {}
self.options = options or {}
self:UpdateOptions(options)
return self
end
function WdItem:setOptions(newOpts)
self:UpdateOptions(newOpts)
end
function WdItem:getStatements(pid, opts)
opts = opts or {}
local vol = opts.vol
local chapter = opts.chapter
if self.snak and self.snak[pid] then
return self.snak[pid]
end
if not self.qid or not mw.wikibase then
return {}
end
if vol or chapter then
return p.getStatementsByVol(self.qid, pid, vol, chapter) or {}
end
if self.options.reqranks and self.options.reqranks.b then
return mw.wikibase.getBestStatements(self.qid, pid) or {}
else
return mw.wikibase.getAllStatements(self.qid, pid) or {}
end
end
function WdItem:getValue(pid, opts)
local mergedOpts = mergeOptions(self.options, opts)
-- Handle local value override
if mergedOpts[2] and mergedOpts[2] ~= "" then
return mergedOpts[2]
end
local statements
if mergedOpts.no_vol_qual then
statements = mw.wikibase.getBestStatements(self.qid, pid)
else
statements = self:getStatements(pid, {vol = mergedOpts.vol, chapter = mergedOpts.chapter})
end
if not statements or #statements == 0 then
return nil
end
local qualID = mw.text.trim(mergedOpts.qual or ""):upper()
if qualID == "" then qualID = nil end
local out = propertyvalueandquals(statements, mergedOpts, qualID)
return assembleoutput(out, mergedOpts, self.qid, pid)
end
function WdItem:getValueFromSnaks(pid, opts)
if not self.snak or not self.snak[pid] then
return nil
end
local mergedOpts = mergeOptions(self.options, opts)
local props = self.snak[pid]
local out = propertyvalueandquals(props, mergedOpts)
return assembleoutput(out, mergedOpts, nil, pid)
end
function WdItem:getPropertyIDs(pid, opts)
local mergedOpts = mergeOptions(self.options, opts)
mergedOpts.noicon = tostring(parseParam(mergedOpts.noicon or "", true))
local statements = self:getStatements(pid, opts)
if not statements or #statements == 0 then return nil end
local onlysrc = parseParam(mergedOpts.onlysourced or mergedOpts.osd, true)
local maxvals = tonumber(mergedOpts.maxvals) or 0
local out = {}
for i, v in ipairs(statements) do
local snak = v.mainsnak
if (snak.datatype == "wikibase-item")
and (v.rank and mergedOpts.reqranks[v.rank:sub(1, 1)])
and (snak.snaktype == "value")
and (sourced(v) or not onlysrc) then
out[#out+1] = snak.datavalue.value.id
end
if maxvals > 0 and #out >= maxvals then break end
end
return assembleoutput(out, mergedOpts, self.qid, pid)
end
function WdItem:getPropOfProp(prop1, prop2, opts)
local mergedOpts = mergeOptions(self.options, opts)
if not self.qid then return nil end
local statements1 = self:getStatements(prop1, opts)
if not statements1 or #statements1 == 0 then return nil end
local onlysrc = parseParam(mergedOpts.onlysourced or mergedOpts.osd, true)
local maxvals = tonumber(mergedOpts.maxvals) or 0
local qualID = mw.text.trim(mergedOpts.qual or ""):upper()
if qualID == "" then qualID = nil end
local out = {}
for k, v in ipairs(statements1) do
if not onlysrc or sourced(v) then
local snak = v.mainsnak
if snak.datatype == "wikibase-item" and snak.snaktype == "value" then
local qid2 = snak.datavalue.value.id
local item2 = WdItem.new(qid2, mergedOpts)
local statements2 = item2:getStatements(prop2, opts)
if statements2 and statements2[1] then
local out2 = propertyvalueandquals(statements2, mergedOpts, qualID)
out[#out+1] = assembleoutput(out2, mergedOpts, qid2, prop2)
end
end
end
if maxvals > 0 and #out >= maxvals then break end
end
return assembleoutput(out, mergedOpts, self.qid, prop1)
end
function WdItem:getLangOfProp(pid)
if not pid or not self.qid then return {} end
local out = {}
local props = mw.wikibase.getAllStatements(self.qid, pid)
for _, v in ipairs(props) do
if v.mainsnak.datatype == "monolingualtext" and v.mainsnak.datavalue then
out[#out + 1] = v.mainsnak.datavalue.value.language
end
end
return out
end
function WdItem:followQid(props, all)
all = parseParam(all, false)
props = (props or ""):upper()
if not self.qid then return nil end
if props == "" then return self.qid end
local out = {}
for p in mw.text.gsplit(props, "%p") do
p = mw.text.trim(p)
for i, v in ipairs(mw.wikibase.getBestStatements(self.qid, p)) do
local linkedid = v.mainsnak.datavalue and v.mainsnak.datavalue.value.id
if linkedid then
if all then
out[#out+1] = linkedid
else
return linkedid
end
end
end
end
if #out > 0 then
return table.concat(out, " ")
else
return self.qid
end
end
-------------------------------------------------------------------------------
-- Public API - Utility Functions
-------------------------------------------------------------------------------
p.getStatementsByVol = function(qid, pid, vol, chapter)
local statements = mw.wikibase.getBestStatements(qid, pid)
if not statements then return nil end
local filtered_statements = {}
if chapter then
for _, statement in ipairs(statements) do
if statement.qualifiers and statement.qualifiers["P792"] then
for _, qual in ipairs(statement.qualifiers["P792"]) do
if qual.datavalue and tostring(qual.datavalue.value) == tostring(chapter) then
table.insert(filtered_statements, statement)
break
end
end
end
end
end
if #filtered_statements == 0 and vol then
for _, statement in ipairs(statements) do
local vol_match = true
if vol and statement.qualifiers and statement.qualifiers["P478"] then
vol_match = false
for _, qual in ipairs(statement.qualifiers["P478"]) do
if qual.datavalue and tostring(qual.datavalue.value) == tostring(vol) then
vol_match = true
break
end
end
end
if vol_match then
table.insert(filtered_statements, statement)
end
end
else
filtered_statements = statements
end
return filtered_statements
end
-------------------------------------------------------------------------------
-- Public API - WdItem Constructors
-------------------------------------------------------------------------------
p.WdItem = WdItem
function p.newItem(qid, opts)
return WdItem.new(qid, opts)
end
function p.currentItem(opts)
return WdItem.new(nil, opts)
end
function p.newSnakItem(snakTable, opts)
return WdItem.newFromSnaks(snakTable, opts)
end
return p