Mòdulu:Bozza/GianAntonucci/Wikidata
This module provides a comprehensive interface for accessing and formatting Wikidata content in MediaWiki templates. It supports all major Wikidata data types and offers extensive customization options while following performance best practices.
Usage
[cancia lu còdici]Note: Unless the from parameter is used, the following examples assume they are being run on a page linked to a Wikidata item. For demonstration purposes, many examples use Douglas Adams (Q42) as a reference.
Basic syntax
[cancia lu còdici]{{#invoke:Bozza/GianAntonucci/Wikidata|function|parameters}}
Quick examples
[cancia lu còdici]{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P31}} → Human
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P569}} → 14 April 1972
{{#invoke:Bozza/GianAntonucci/Wikidata|getQualifier|P39|P580}} → 20 January 2009
Functions
[cancia lu còdici]getProperty
[cancia lu còdici]Retrieves and formats property values from Wikidata.
Parameters:
1orproperty– Property ID (required)from– Entity ID to get data from (optional, defaults to current page)rank– Which ranks to include (see rank parameter below)formatting– Output format (see data type formatting below)separator– String to join multiple values (default:,)limit– Maximum number of values to returnindex– Return only the nth valuehasqualifier– Only return statements with this qualifier (see filtering parameters)qualifiervalue– Required value for the qualifier (used withhasqualifier)default– Value to return if no data found
Examples:
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P31}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P31|formatting=label}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P31|from=Q42}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P39|hasqualifier=P580}}
getQualifier
[cancia lu còdici]Retrieves qualifier values from property statements.
Parameters:
1orproperty– Property ID (required)2orqualifier– Qualifier ID (required)from– Entity ID (optional, defaults to current page)rank– Which ranks to include (see rank parameter)formatting– How to format the outputseparator– String to join multiple values (default:,)default– Value to return if no data found
Examples:
{{#invoke:Bozza/GianAntonucci/Wikidata|getQualifier|P39|P580}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getQualifier|P39|P580|separator=<br/>}}
getLabel
[cancia lu còdici]Gets the label of a Wikidata entity.
Parameters:
1orentity– Entity ID (optional, defaults to current page)2orlang– Language code (optional, defaults to wiki language)
Examples:
{{#invoke:Bozza/GianAntonucci/Wikidata|getLabel}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getLabel|Q42}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getLabel|Q42|de}}
getDescription
[cancia lu còdici]Gets the description of a Wikidata entity.
Parameters:
1orentity– Entity ID (optional, defaults to current page)2orlang– Language code (optional, defaults to wiki language)
Examples:
{{#invoke:Bozza/GianAntonucci/Wikidata|getDescription|Q42}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getDescription|Q42|fr}}
getTerm
[cancia lu còdici]Gets both label and description efficiently.
Parameters:
1orentity– Entity ID (optional, defaults to current page)2orlang– Language code (optional)separator– String between label and description (default:-)
Examples:
{{#invoke:Bozza/GianAntonucci/Wikidata|getTerm|Q42}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getTerm|Q42|separator=: }}
getId
[cancia lu còdici]Gets the Wikidata entity ID for a page.
Parameters:
1orpage– Page title (optional, defaults to current page)
Examples:
{{#invoke:Bozza/GianAntonucci/Wikidata|getId}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getId|Douglas Adams}}
count
[cancia lu còdici]Counts the number of statements for a property.
Parameters:
1orproperty– Property ID (required)- All filtering parameters from
getPropertyare supported
Examples:
{{#invoke:Bozza/GianAntonucci/Wikidata|count|P50}}
{{#invoke:Bozza/GianAntonucci/Wikidata|count|P50|hasqualifier=P1545}}
Common parameters
[cancia lu còdici]Formatting parameter
[cancia lu còdici]The formatting parameter controls how values are displayed:
For entities:
raworid– Returns the entity ID (e.g.,Q42)label– Returns only the labelsitelink– Returns only the sitelink- (default) – Returns a linked label if possible
For other types:
raw– Returns unformatted value- (default) – Returns formatted value
Rank parameter
[cancia lu còdici]Controls which statement ranks to include:
best– Preferred if available, otherwise normal (default)preferred– Only preferred ranknormal– Only normal rankdeprecated– Only deprecated rankall– All ranks
Filtering parameters
[cancia lu còdici]hasqualifier– Only include statements with this qualifierqualifiervalue– Required value for the qualifierlimit– Maximum number of resultsindex– Select only the nth result
Data type formatting
[cancia lu còdici]Entities (items and properties)
[cancia lu còdici]| Code | Result |
|---|---|
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P31|from=Q42}}
|
èssiri umanu |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P31|from=Q42|formatting=label}}
|
èssiri umanu |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P31|from=Q42|formatting=id}}
|
Q5 |
Quantities
[cancia lu còdici]| Code | Result |
|---|---|
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P2048|from=Q42}}
|
1,96 metru |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P2048|from=Q42|unitsymbol=true}}
|
1,96 metru |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P2048|from=Q42|showunit=false}}
|
1,96 metru |
Dates and times
[cancia lu còdici]| Code | Result |
|---|---|
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P569|from=Q42}}
|
11 marzu 1952 |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P570|from=Q42}}
|
11 maju 2001 |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P569|from=Q1}}
|
Different precisions are automatically formatted:
| Precision | Example output |
|---|---|
| Day | 11 May 2001 |
| Month | May 2001 |
| Year | 2001 |
| Decade | 2000s |
| Century | 21st century |
| Millennium | 3rd millennium |
Commons media
[cancia lu còdici]URLs
[cancia lu còdici]| Code | Result |
|---|---|
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P856|from=Q42}}
|
douglasadams.com |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P856|from=Q42|formatting=raw}}
|
https://douglasadams.com |
Coordinates
[cancia lu còdici]| Code | Result |
|---|---|
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P625|from=Q174373|limit=1}}
|
9.215, 123.514 |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P625|from=Q174373|limit=1|coord=latitude}}
|
9.215, 123.514 |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P625|from=Q174373|limit=1|coord=longitude}}
|
9.215, 123.514 |
Monolingual text
[cancia lu còdici]| Code | Result |
|---|---|
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P1477|from=Q42}}
|
Douglas Noël Adams |
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P1477|from=Q42|showlang=true}}
|
Douglas Noël Adams (en) |
Advanced features
[cancia lu còdici]Multiple values
[cancia lu còdici]Control how multiple values are displayed:
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P50|separator= / }}
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P50|limit=3}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P50|index=2}}
Qualifier filtering
[cancia lu còdici]Filter statements by qualifiers:
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P39|hasqualifier=P580}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P39|hasqualifier=P580|qualifiervalue=2020}}
Using specific entities
[cancia lu còdici]Access data from entities other than the current page:
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P31|from=Q42}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getLabel|Q42}}
{{#invoke:Bozza/GianAntonucci/Wikidata|getDescription|Q42}}
Examples
[cancia lu còdici]Infobox integration
[cancia lu còdici]{{Infobox person
| name = {{#invoke:Bozza/GianAntonucci/Wikidata|getLabel}}
| image = {{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P18|size=250px}}
| birth_date = {{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P569}}
| birth_place = {{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P19|formatting=label}}
| occupation = {{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P106|separator=<br/>}}
}}
Conditional display
[cancia lu còdici]{{#if: {{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P50}} |
'''Authors:''' {{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P50|separator=, }}
}}
Complex queries
[cancia lu còdici]Mayor: {{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P6|hasqualifier=P580}}
Term start: {{#invoke:Bozza/GianAntonucci/Wikidata|getQualifier|P6|P580}}
Term end: {{#invoke:Bozza/GianAntonucci/Wikidata|getQualifier|P6|P582}}
Performance considerations
[cancia lu còdici]- Efficient API usage: The module uses
getBestStatementsandgetAllStatementsinstead ofgetEntity, which is much more memory-efficient. - Lazy loading: Data is only fetched when needed, not preloaded.
- Caching: MediaWiki caches Wikidata access, so repeated calls to the same property are efficient.
- Filtering: The module filters data as early as possible to minimize processing.
Error handling
[cancia lu còdici]The module provides clear error messages:
- Property not provided – Shown when the property parameter is missing
- Qualifier not provided – Shown when the qualifier parameter is missing
- Entity not found – Shown when the specified entity doesn't exist
- Unknown data type – Shown for unsupported Wikidata types
- Unknown entity type – Shown for unrecognised entity types
Errors are wrapped in <span class="error"> tags and can trigger categorization in mainspace.
Suppressing errors
[cancia lu còdici]Use the default parameter to provide fallback text:
{{#invoke:Bozza/GianAntonucci/Wikidata|getProperty|P999|default=No data available}}
See also
[cancia lu còdici]- Wikibase Client/Lua documentation
- List of Wikidata properties
- Module:Wikidata – Alternative Wikidata module
-- Custom Wikidata Module (Fully Fixed Version)
-- Based on best practices from multiple implementations
--
-- This module provides a comprehensive interface to Wikidata for MediaWiki templates.
-- It supports fetching and formatting all major Wikidata data types with extensive
-- customization options.
--
-- FIXED ISSUES:
-- * Correct qualifier filtering (filter BEFORE rank selection)
-- * Eliminated N+1 query problem for unit symbols
-- * Fixed date formatting for large timespans (corrected arithmetic)
-- * Improved year-zero handling
-- * DRY principle applied to repeated code
-- * Better entity type support
--
-- Basic usage:
-- {{#invoke:WikidataModule|getProperty|P31}}
-- {{#invoke:WikidataModule|getProperty|property=P31|formatting=label}}
-- {{#invoke:WikidataModule|getQualifier|P580|P585}}
require('strict')
local p = {}
-- Configuration
local config = {
-- Error messages
errors = {
['property-not-provided'] = 'Property parameter not provided',
['qualifier-not-provided'] = 'Qualifier parameter not provided',
['entity-not-found'] = 'Entity not found',
['unknown-datatype'] = 'Unknown data type',
['unknown-entity-type'] = 'Unknown entity type',
['no-claims'] = 'No claims found'
},
-- Special value labels
specialValues = {
somevalue = "''unknown value''",
novalue = "''no value''"
},
-- Supported calendar models
calendars = {
['http://www.wikidata.org/entity/Q1985727'] = 'gregorian',
['http://www.wikidata.org/entity/Q11184'] = 'julian'
}
}
-- Helper function for error handling
local function handleError(code)
local message = config.errors[code] or code
local namespace = mw.title.getCurrentTitle().namespace
local category = namespace == 0 and '[[Category:Pages with Wikidata errors]]' or ''
return string.format('<span class="error">%s</span>%s', message, category)
end
-- Helper function to extract args from frame (DRY principle)
local function getArgs(frame, ...)
local paramNames = {...}
local args = {}
-- Check if we have any of the specified parameters in frame.args
local hasDirectParams = false
for _, param in ipairs(paramNames) do
if frame.args[param] then
hasDirectParams = true
break
end
end
-- Also check for positional parameter
if frame.args[1] then
hasDirectParams = true
end
if hasDirectParams then
args = frame.args -- Direct invoke
else
args = frame:getParent().args -- Called from template
end
return args
end
-- Helper function to generate ordinal suffix (DRY principle)
local function getOrdinalSuffix(n)
if n % 100 >= 11 and n % 100 <= 13 then
return 'th'
elseif n % 10 == 1 then
return 'st'
elseif n % 10 == 2 then
return 'nd'
elseif n % 10 == 3 then
return 'rd'
else
return 'th'
end
end
-- Get entity prefix based on entity type (expanded to support more types)
local function getEntityPrefix(entityType)
local prefixes = {
item = 'Q',
property = 'P',
lexeme = 'L',
form = 'F',
sense = 'S'
}
return prefixes[entityType]
end
-- Format entity ID (item or property)
local function formatEntityId(entityId, args)
if args.formatting == 'raw' or args.formatting == 'id' then
return entityId
end
local label = mw.wikibase.getLabel(entityId)
local sitelink = entityId:sub(1,1) == 'Q' and mw.wikibase.getSitelink(entityId) or nil
if args.formatting == 'label' then
return label or entityId
elseif args.formatting == 'sitelink' then
return sitelink or ''
end
-- Default formatting with link
if sitelink then
return label and string.format('[[%s|%s]]', sitelink, label) or string.format('[[%s]]', sitelink)
elseif label then
return label
else
return entityId
end
end
-- Format time values with proper precision handling (FIXED)
local function formatTime(timeValue, precision, args)
local time = timeValue.time
local calendar = config.calendars[timeValue.calendarmodel] or 'gregorian'
-- Extract year, month, day from ISO format
local year, month, day = string.match(time, '([+-]?%d+)%-(%d+)%-(%d+)')
year = tonumber(year)
month = tonumber(month)
day = tonumber(day)
if not year then return '' end
-- Handle different precision levels
if precision <= 5 then -- Large timespans (FIXED calculation)
local scales = {
[0] = {1000000000, "billion years"},
[1] = {100000000, "hundred million years"},
[2] = {10000000, "ten million years"},
[3] = {1000000, "million years"},
[4] = {100000, "hundred thousand years"},
[5] = {10000, "ten thousand years"}
}
local scale = scales[precision]
if not scale then
-- Fallback for unexpected precision values
return tostring(math.abs(year)) .. (year < 0 and ' BCE' or '')
end
local value = math.abs(year) / scale[1]
local suffix = year < 0 and ' BCE' or ''
-- Format with appropriate decimal places
local formatted
if value >= 10 then
formatted = string.format('%.0f %s', value, scale[2])
else
formatted = string.format('%.1f %s', value, scale[2])
end
return formatted .. suffix
elseif precision == 6 then -- Millennium
local millennium = math.floor((math.abs(year) - 1) / 1000) + 1
local ordinal = millennium .. getOrdinalSuffix(millennium)
return ordinal .. ' millennium' .. (year < 0 and ' BCE' or '')
elseif precision == 7 then -- Century
local century = math.floor((math.abs(year) - 1) / 100) + 1
local ordinal = century .. getOrdinalSuffix(century)
return ordinal .. ' century' .. (year < 0 and ' BCE' or '')
elseif precision == 8 then -- Decade
local decade = math.floor(math.abs(year) / 10) * 10
return decade .. 's' .. (year < 0 and ' BCE' or '')
elseif precision == 9 then -- Year
return math.abs(year) .. (year < 0 and ' BCE' or '')
else -- Month or more precise
-- Better handling of zero values (FIXED)
if month == 0 then month = 1 end
if day == 0 then day = 1 end
-- Reconstruct the time string with fixed values
time = string.format('%+05d-%02d-%02dT00:00:00Z', year, month, day)
local lang = mw.language.getContentLanguage()
local formatStr
if precision == 10 then -- Month
formatStr = 'F Y'
elseif precision == 11 then -- Day
formatStr = 'j F Y'
elseif precision >= 12 then -- Hour or more precise
formatStr = 'j F Y, H:i' .. (precision >= 14 and ':s' or '')
end
-- Handle negative years for MediaWiki date formatting
if year < 0 then
-- Use positive year for formatting
time = string.format('%+05d-%02d-%02dT00:00:00Z', -year, month, day)
local formatted = lang:formatDate(formatStr, time)
return formatted .. ' BCE'
else
return lang:formatDate(formatStr, time)
end
end
end
-- Get unit symbol from label (FIXED - no more N+1 queries)
local function getUnitText(unitId, args)
-- Default to using the label (much more efficient)
local unitText = mw.wikibase.getLabel(unitId) or ''
-- Only fetch the symbol via P5061 if explicitly requested
if args.fetchsymbol and unitText ~= '' then
local statements = mw.wikibase.getBestStatements(unitId, 'P5061')
if statements and statements[1] and statements[1].mainsnak.snaktype == 'value' then
-- P5061 returns monolingual text, preferably get 'en' or 'mul' (multilingual)
for _, statement in ipairs(statements) do
local lang = statement.mainsnak.datavalue.value.language
if lang == 'mul' or lang == 'en' then
unitText = statement.mainsnak.datavalue.value.text
break
end
end
-- If no English or multilingual, use first available
if unitText == '' and statements[1] then
unitText = statements[1].mainsnak.datavalue.value.text
end
end
end
return unitText
end
-- Format quantity values
local function formatQuantity(value, args)
local amount = tonumber(value.amount)
if args.round then
local mult = 10^(args.round or 0)
amount = math.floor(amount * mult + 0.5) / mult
end
if args.formatnum ~= false then
amount = mw.language.getContentLanguage():formatNum(amount)
end
-- Handle units if present
if value.unit and value.unit ~= '1' then
local unitId = string.match(value.unit, 'Q%d+')
if unitId and args.showunit ~= false then
local unitText = getUnitText(unitId, args)
if unitText ~= '' then
amount = amount .. ' ' .. unitText
end
end
end
return tostring(amount)
end
-- Format URL values
local function formatUrl(url, args)
if args.formatting == 'raw' then
return url
end
-- Basic URL formatting with line break opportunity
return string.format('<span class="url">[%s %s]</span>', url, url:gsub('^https?://', ''))
end
-- Format Commons media filenames
local function formatCommonsMedia(filename, args)
if args.formatting == 'raw' then
return filename
end
-- Build image options
local parts = { 'File:' .. filename }
-- Size/format options
if args.thumb then
table.insert(parts, 'thumb')
elseif args.size then
table.insert(parts, args.size)
else
table.insert(parts, 'frameless')
end
-- Alignment
if args.align then
table.insert(parts, args.align)
end
-- Alt text
if args.alt then
table.insert(parts, 'alt=' .. args.alt)
end
-- Caption (must be last)
if args.caption then
table.insert(parts, args.caption)
end
return string.format('[[%s]]', table.concat(parts, '|'))
end
-- Main function to format data values
local function formatDatavalue(datavalue, datatype, args)
if datavalue.type == 'wikibase-entityid' then
local value = datavalue.value
local prefix = getEntityPrefix(value['entity-type'])
if not prefix then
return handleError('unknown-entity-type')
end
local entityId = prefix .. value['numeric-id']
return formatEntityId(entityId, args)
elseif datavalue.type == 'string' then
if datatype == 'url' then
return formatUrl(datavalue.value, args)
elseif datatype == 'commonsMedia' then
return formatCommonsMedia(datavalue.value, args)
else
-- Regular string, external-id, or math
return datavalue.value
end
elseif datavalue.type == 'time' then
return formatTime(datavalue.value, datavalue.value.precision, args)
elseif datavalue.type == 'quantity' then
return formatQuantity(datavalue.value, args)
elseif datavalue.type == 'monolingualtext' then
local text = datavalue.value.text
local lang = datavalue.value.language
if args.showlang then
return string.format('<span lang="%s">%s</span> (%s)', lang, text, lang)
else
return text
end
elseif datavalue.type == 'globecoordinate' then
local lat = datavalue.value.latitude
local lon = datavalue.value.longitude
if args.formatting == 'dms' then
-- Convert to DMS format
local function toDMS(coord, isLat)
local dir = isLat and (coord >= 0 and 'N' or 'S') or (coord >= 0 and 'E' or 'W')
coord = math.abs(coord)
local deg = math.floor(coord)
local min = math.floor((coord - deg) * 60)
local sec = ((coord - deg) * 60 - min) * 60
return string.format('%d°%d\'%.2f"%s', deg, min, sec, dir)
end
return toDMS(lat, true) .. ', ' .. toDMS(lon, false)
else
-- Round decimals
lat = math.floor(lat * 1000000 + 0.5) / 1000000
lon = math.floor(lon * 1000000 + 0.5) / 1000000
return string.format('%s, %s', lat, lon)
end
else
return handleError('unknown-datatype')
end
end
-- Format a single snak
local function formatSnak(snak, args)
if snak.snaktype == 'somevalue' then
return config.specialValues.somevalue
elseif snak.snaktype == 'novalue' then
return config.specialValues.novalue
elseif snak.snaktype == 'value' then
return formatDatavalue(snak.datavalue, snak.datatype, args)
end
return ''
end
-- Get and filter claims (FIXED - proper qualifier filtering)
local function getClaims(propertyId, args)
local entityId = args.from or mw.wikibase.getEntityIdForCurrentPage()
if not entityId then
return nil
end
-- FIXED: Always get ALL statements first when filtering is needed
local claims
if args.hasqualifier or args.qualifiervalue then
-- Get ALL statements for proper filtering
claims = mw.wikibase.getAllStatements(entityId, propertyId)
else
-- No filtering needed, can use optimized methods
if args.rank == 'best' or not args.rank then
claims = mw.wikibase.getBestStatements(entityId, propertyId)
elseif args.rank == 'all' then
claims = mw.wikibase.getAllStatements(entityId, propertyId)
else
-- Get all and filter by specific rank
claims = mw.wikibase.getAllStatements(entityId, propertyId)
local filtered = {}
for _, claim in ipairs(claims) do
if claim.rank == args.rank then
table.insert(filtered, claim)
end
end
claims = filtered
end
end
if not claims or #claims == 0 then
return nil
end
-- Filter by qualifier presence if specified
if args.hasqualifier then
local filtered = {}
for _, claim in ipairs(claims) do
if claim.qualifiers and claim.qualifiers[args.hasqualifier] then
table.insert(filtered, claim)
end
end
claims = filtered
end
-- Filter by qualifier value if specified
if args.qualifiervalue and args.hasqualifier and #claims > 0 then
local filtered = {}
for _, claim in ipairs(claims) do
if claim.qualifiers and claim.qualifiers[args.hasqualifier] then
for _, qualifier in ipairs(claim.qualifiers[args.hasqualifier]) do
local value = formatSnak(qualifier, {formatting = 'raw'})
if value == args.qualifiervalue then
table.insert(filtered, claim)
break
end
end
end
end
claims = filtered
end
-- NOW apply rank filtering if we had qualifier filtering
if (args.hasqualifier or args.qualifiervalue) and args.rank and args.rank ~= 'all' and #claims > 0 then
if args.rank == 'best' then
-- Manually determine best rank
local bestRank = 'normal'
for _, claim in ipairs(claims) do
if claim.rank == 'preferred' then
bestRank = 'preferred'
break
elseif claim.rank == 'normal' then
bestRank = 'normal'
end
end
-- Filter to only best rank
local filtered = {}
for _, claim in ipairs(claims) do
if claim.rank == bestRank then
table.insert(filtered, claim)
end
end
claims = filtered
else
-- Filter by specific rank
local filtered = {}
for _, claim in ipairs(claims) do
if claim.rank == args.rank then
table.insert(filtered, claim)
end
end
claims = filtered
end
end
-- Limit number of results if specified
if args.limit and #claims > tonumber(args.limit) then
local limited = {}
for i = 1, tonumber(args.limit) do
limited[i] = claims[i]
end
claims = limited
end
-- Select specific index if specified
if args.index and #claims > 0 then
local idx = tonumber(args.index)
if idx and idx > 0 and idx <= #claims then
claims = { claims[idx] }
else
claims = {}
end
end
return claims
end
-- Format statements
local function formatStatements(claims, args)
local results = {}
for _, claim in ipairs(claims) do
local value = formatSnak(claim.mainsnak, args)
if value ~= '' then
table.insert(results, value)
end
end
if #results == 0 then
return nil
end
-- Join results
local separator = args.separator or ', '
return table.concat(results, separator)
end
-------------------------------------------------------------------------------
-- Public functions
-------------------------------------------------------------------------------
---
-- Get property value(s) from Wikidata
function p._getProperty(args)
local propertyId = args[1] or args.property
if not propertyId then
return handleError('property-not-provided')
end
-- Normalize property ID
propertyId = string.upper(propertyId)
-- Get claims
local claims = getClaims(propertyId, args)
if not claims or #claims == 0 then
return args.default or ''
end
-- Format and return
return formatStatements(claims, args) or args.default or ''
end
-- Template interface
function p.getProperty(frame)
local args = getArgs(frame, 'property')
return p._getProperty(args)
end
---
-- Get qualifier value(s) from statements (FIXED - removed redundant check)
function p._getQualifier(args)
local propertyId = args[1] or args.property
local qualifierId = args[2] or args.qualifier
if not propertyId then
return handleError('property-not-provided')
end
if not qualifierId then
return handleError('qualifier-not-provided')
end
propertyId = string.upper(propertyId)
qualifierId = string.upper(qualifierId)
-- Get claims already filtered by qualifier presence
local getClaims_args = {
from = args.from,
rank = args.rank or 'best',
hasqualifier = qualifierId
}
local claims = getClaims(propertyId, getClaims_args)
if not claims or #claims == 0 then
return args.default or ''
end
-- Extract qualifier values (no redundant check needed)
local results = {}
for _, claim in ipairs(claims) do
-- We know the qualifier exists because getClaims filtered for it
for _, qualifier in ipairs(claim.qualifiers[qualifierId]) do
local value = formatSnak(qualifier, args)
if value ~= '' then
table.insert(results, value)
end
end
end
if #results == 0 then
return args.default or ''
end
local separator = args.separator or ', '
return table.concat(results, separator)
end
-- Template interface
function p.getQualifier(frame)
local args = getArgs(frame, 'property', 'qualifier')
return p._getQualifier(args)
end
---
-- Get Wikidata entity ID for current page or specified title
function p._getId(args)
local title = args[1] or args.page
if title then
local titleObj = mw.title.new(title)
if titleObj then
return mw.wikibase.getEntityIdForTitle(titleObj.prefixedText) or ''
end
end
return mw.wikibase.getEntityIdForCurrentPage() or ''
end
-- Template interface
function p.getId(frame)
local args = getArgs(frame, 'page')
return p._getId(args)
end
---
-- Get label of a Wikidata entity
function p._getLabel(args)
local entityId = args[1] or args.entity or args.from
local lang = args[2] or args.lang
if not entityId then
entityId = mw.wikibase.getEntityIdForCurrentPage()
end
if not entityId then
return ''
end
entityId = string.upper(entityId)
if lang then
return mw.wikibase.getLabelByLang(entityId, lang) or ''
else
return mw.wikibase.getLabel(entityId) or ''
end
end
-- Template interface
function p.getLabel(frame)
local args = getArgs(frame, 'entity', 'from')
return p._getLabel(args)
end
---
-- Get description of a Wikidata entity
function p._getDescription(args)
local entityId = args[1] or args.entity or args.from
local lang = args[2] or args.lang
if not entityId then
entityId = mw.wikibase.getEntityIdForCurrentPage()
end
if not entityId then
return ''
end
entityId = string.upper(entityId)
if lang then
local term = mw.wikibase.getTermByLang(entityId, lang)
return term and term.description or ''
else
return mw.wikibase.getDescription(entityId) or ''
end
end
-- Template interface
function p.getDescription(frame)
local args = getArgs(frame, 'entity', 'from')
return p._getDescription(args)
end
---
-- Get both label and description efficiently
function p._getTerm(args)
local entityId = args[1] or args.entity or args.from
local lang = args[2] or args.lang
local separator = args.separator or ' - '
if not entityId then
entityId = mw.wikibase.getEntityIdForCurrentPage()
end
if not entityId then
return ''
end
entityId = string.upper(entityId)
local label, description
if lang then
local term = mw.wikibase.getTermByLang(entityId, lang)
if term then
label = term.label
description = term.description
end
else
label = mw.wikibase.getLabel(entityId)
description = mw.wikibase.getDescription(entityId)
end
if label and description then
return label .. separator .. description
elseif label then
return label
elseif description then
return description
else
return ''
end
end
-- Template interface
function p.getTerm(frame)
local args = getArgs(frame, 'entity', 'from')
return p._getTerm(args)
end
---
-- Count number of statements for a property (now works correctly with qualifiers)
function p._count(args)
local propertyId = args[1] or args.property
if not propertyId then
return '0'
end
propertyId = string.upper(propertyId)
local claims = getClaims(propertyId, args)
return tostring(claims and #claims or 0)
end
-- Template interface
function p.count(frame)
local args = getArgs(frame, 'property')
return p._count(args)
end
return p
