Modul:Wikidata/qualifiers
Görünüş
Bu modulun sənədləşdirmə səhifəsi Modul:Wikidata/qualifiers/doc səhifəsində yaradıla bilər
local config = require( 'Module:Wikidata/qualifiers/config' )
local Ordinal = require( 'Modul:Ordinal' )
local p = {}
function p.getValues( statement, property )
return statement and statement.qualifiers and statement.qualifiers[ property ] or {}
end
function p.getFirstWithDataValue( statement, property )
for _, snak in ipairs( p.getValues( statement, property ) ) do
if snak.snaktype ~= 'novalue' and snak.snaktype ~= 'somevalue' and snak.datavalue then
return snak
end
end
end
function p.getTimeBoundariesFromQualifier( context, statement, property )
local left, right
for _, snak in ipairs( p.getValues( statement, property ) ) do
local dates = context.parseTimeBoundariesFromSnak( snak )
if not dates then return nil end
left = left and math.min( left, dates[ 1 ] ) or dates[ 1 ]
right = right and math.max( right, dates[ 2 ] ) or dates[ 2 ]
end
if left and right then return { left, right } end
end
function p.getTimeBoundariesFromQualifiers( context, statement, properties, pointProperty )
for _, property in ipairs( properties or config.boundaryPriority ) do
local dates = p.getTimeBoundariesFromQualifier( context, statement, property )
if dates then return property == pointProperty and { dates[ 1 ], dates[ 1 ] } or dates end
end
end
function p.getLabelTimeBoundaries( context, statement )
return p.getTimeBoundariesFromQualifiers( context, statement, config.labelBoundaryPriority, 'P582' )
end
function p.getSourcingCircumstances( statement )
local result, seen = {}, {}
for _, snak in ipairs( p.getValues( statement, 'P1480' ) ) do
local value = snak.snaktype ~= 'novalue' and snak.snaktype ~= 'somevalue' and
snak.datavalue and snak.datavalue.value
local id = type( value ) == 'table' and value.id
if id and not seen[ id ] then
seen[ id ] = true
table.insert( result, id )
end
end
return result
end
function p.getI18n( defaults, overrides )
local result = {}
for _, values in ipairs( { config.circumstances, defaults or {}, overrides or {} } ) do
for key, value in pairs( values ) do result[ key ] = value end
end
return result
end
function p.applyCircumstances( options, result, circumstances )
if not result or result == '' then return result end
local seen = {}
for _, id in ipairs( circumstances or {} ) do
if not seen[ id ] then
seen[ id ] = true
local prefix = options.i18n and options.i18n[ id ]
if prefix == nil then prefix = config.circumstances[ id ] end
if type( prefix ) == 'string' then result = prefix .. result end
end
end
return result
end
local function yearFromSnak( snak )
local value = snak and snak.datavalue and snak.datavalue.type == 'time' and snak.datavalue.value
if type( value ) == 'table' and type( value.time ) == 'string' then
return tonumber( string.match( value.time, '^([+-]?%d+)%-' ) )
end
end
function p.getPeriod( statement )
return {
from = yearFromSnak( p.getFirstWithDataValue( statement, 'P580' ) ),
to = yearFromSnak( p.getFirstWithDataValue( statement, 'P582' ) ),
point = yearFromSnak( p.getFirstWithDataValue( statement, 'P585' ) ),
qualifiers = statement.qualifiers,
}
end
function p.renderRange( range, options )
options = options or {}
if options.endyearonly and range.to and not range.from then return tostring( range.to ) end
if options.startyearonly and range.from and not range.to then return tostring( range.from ) end
if range.from and range.to then
if range.from == range.to then return tostring( range.from ) end
return range.from .. config.rangeSeparator .. range.to
elseif range.from then
return ( type( range.from ) == 'number' and Ordinal._ordinal( range.from ) or range.from ) .. config.fromSuffix
elseif range.to then
return ( type( range.to ) == 'number' and Ordinal._ordinal( range.to ) or range.to ) .. config.toSuffix
elseif range.point then
return tostring( range.point )
end
end
local function periodStart( range )
return range.from or range.point or 0
end
local function sortPeriods( ranges )
table.sort( ranges, function( a, b )
local first, second = periodStart( a ), periodStart( b )
return first < second or first == second and a.index < b.index
end )
end
local function parenthesize( text, options )
if text == '' then return '' end
local style = options.qualifierStyle
if style == nil then style = config.style end
if style == false or style == '' then return ' (' .. text .. ')' end
return ' <span style="' .. style .. '">(' .. text .. ')</span>'
end
local function formatValues( context, options, statement, property )
local snaks = p.getValues( statement, property )
if #snaks == 0 then return '', false end
local params = context.getPropertyParams( property, snaks[ 1 ].datatype, {} )
params.property = property
params.i18n = options.i18n
params.entity, params.frame = options.entity, options.frame
local result = {}
for _, snak in ipairs( snaks ) do
local year = not params.invisible and options.qualifierPrecision ~= 'full' and yearFromSnak( snak )
local value = year and tostring( year ) or context.formatSnak( params, snak )
if value and value ~= '' then table.insert( result, value ) end
end
return table.concat( result, config.separator ), params.invisible
end
function p.appendQualifiers( context, options, statement, result )
if not result or result == '' then return result end
local first, firstInvisible = formatValues( context, options, statement, options.firstqualifier )
local second, secondInvisible = formatValues( context, options, statement, options.secondqualifier )
if firstInvisible then result, first = result .. first, '' end
if secondInvisible then result, second = result .. second, '' end
if first ~= '' or second ~= '' then
local range = first == second and first or first .. config.rangeSeparator .. second
result = result .. parenthesize( range, options )
end
if first == '' and options.qualifier and options.qualifier ~= options.firstqualifier and
options.qualifier ~= options.secondqualifier
then
local value, invisible = formatValues( context, options, statement, options.qualifier )
result = result .. ( invisible and value or parenthesize( value, options ) )
end
return result
end
function p.formatStatement( context, options, statement )
local valueOptions = context.cloneOptions( options )
valueOptions.qualifiers = statement.qualifiers
local result = context.formatSnak( valueOptions, statement.mainsnak, p.getSourcingCircumstances( statement ) )
result = p.appendQualifiers( context, valueOptions, statement, result )
if result and result ~= '' and options.references then
result = result .. context.formatRefs( options, statement )
end
return result
end
local function entityId( statement )
local snak = statement and statement.mainsnak
local value = snak and snak.snaktype == 'value' and snak.datavalue and
snak.datavalue.type == 'wikibase-entityid' and snak.datavalue.value
return type( value ) == 'table' and value.id or nil
end
local function groupClaims( claims, preferredOnly )
local groups, order = {}, {}
for index, statement in ipairs( claims or {} ) do
local id = entityId( statement )
if id and statement.rank ~= 'deprecated' and ( not preferredOnly or statement.rank == 'preferred' ) then
if not groups[ id ] then
groups[ id ] = { snak = statement.mainsnak, claimId = statement.id, ranges = {}, statements = {}, index = index }
table.insert( order, id )
end
local range = p.getPeriod( statement )
range.index = index
table.insert( groups[ id ].ranges, range )
table.insert( groups[ id ].statements, statement )
end
end
for _, id in ipairs( order ) do sortPeriods( groups[ id ].ranges ) end
return groups, order
end
local function getLabelQualifiers( context, ranges )
local latest, qualifiers
for _, range in ipairs( ranges ) do
local dates = p.getLabelTimeBoundaries( context, { qualifiers = range.qualifiers } )
if dates and ( not latest or dates[ 1 ] > latest ) then
latest, qualifiers = dates[ 1 ], range.qualifiers
end
end
return qualifiers
end
local function renderGroups( context, options, groups, order, merged )
table.sort( order, function( a, b )
local first, second = periodStart( groups[ a ].ranges[ 1 ] ), periodStart( groups[ b ].ranges[ 1 ] )
return first < second or first == second and groups[ a ].index < groups[ b ].index
end )
local items = {}
for _, id in ipairs( order ) do
local group = groups[ id ]
local valueOptions = context.cloneOptions( options )
valueOptions.qualifiers = getLabelQualifiers( context, group.ranges )
local circumstances = #group.statements == 1 and p.getSourcingCircumstances( group.statements[ 1 ] ) or nil
local label = context.formatSnak( valueOptions, group.snak, circumstances )
if label and label ~= '' then
local parts, seen = {}, {}
for _, range in ipairs( group.ranges ) do
local text = p.renderRange( range, options )
if text and not seen[ text ] then
seen[ text ] = true
table.insert( parts, text )
end
if range.from and not range.to then break end
end
label = label .. parenthesize( table.concat( parts, config.separator ), options )
if options.references then
label = label .. context.formatMergedRefs( options, group.statements )
end
local property = string.upper( string.match( options.property, '^[Pp]%d+' ) )
label = context.wrapStatement( label, property, group.claimId )
table.insert( items, label )
end
end
if #items == 0 then return '' end
local before = options.before or ( merged and '<ul><li>' or '' )
local separator = options.separator or ( merged and '</li><li>' or config.separator )
local after = options.after or ( merged and '</li></ul>' or '' )
return before .. table.concat( items, separator ) .. after
end
function p.formatMergedProperty( context, options )
local claims = context.selectClaims( options, options.property .. ( options.rank or '[!rank:deprecated]' ) )
local groups, order = groupClaims( claims )
return renderGroups( context, options, groups, order, true )
end
function p.formatAllPreferredClaims( context, options, claims, order )
local groups, preferredOrder = groupClaims( claims, true )
local selected = {}
for _, id in ipairs( order or preferredOrder ) do
if groups[ id ] then table.insert( selected, id ) end
end
return renderGroups( context, options, groups, selected, false )
end
function p.formatMostRecentProperty( context, options )
local claims = context.selectClaims( options, options.property .. '[rank:preferred, rank:normal]' )
if not claims or #claims == 0 then return '' end
if options.allpreferred then
local _, order = groupClaims( claims, true )
if #order > 1 then return p.formatAllPreferredClaims( context, options, claims, order ) end
end
local latest, maxTimestamp
for _, statement in ipairs( claims ) do
if statement.rank ~= 'deprecated' then
local timestamp
for _, property in ipairs( { 'P585', 'P580' } ) do
local snak = p.getFirstWithDataValue( statement, property )
if snak then timestamp = context.parseTimeFromSnak( snak ) end
if timestamp then break end
end
if timestamp and ( not maxTimestamp or timestamp > maxTimestamp ) then
latest, maxTimestamp = statement, timestamp
end
end
end
if not latest then return context.formatPropertyDefault( context, options ) end
local id = entityId( latest )
if id then
local groups = groupClaims( claims )
return renderGroups( context, options, groups, { id }, false )
end
local range = p.getPeriod( latest )
range.index = 1
local groups = { { snak = latest.mainsnak, claimId = latest.id, ranges = { range }, statements = { latest }, index = 1 } }
return renderGroups( context, options, groups, { 1 }, false )
end
function p.formatQuantityWithDateClaim( context, options, statement )
local valueOptions = context.cloneOptions( options )
valueOptions.qualifier = 'P585'
valueOptions.qualifierPrecision = 'full'
valueOptions.qualifierStyle = false
return p.formatStatement( context, valueOptions, statement )
end
function p.formatDateRange( first, second )
local function year( value )
if not value or value == '' then return nil end
local text = tostring( value ):gsub( '%[%[', '' ):gsub( '%]%]', '' )
local found = text:match( '(%d%d%d%d)' )
if found == '0369' then return '?' end
return found and tonumber( found ) or text:match( '^%s*(.-)%s*$' )
end
return p.renderRange( { from = year( first ), to = year( second ) } ) or ''
end
p.formatPropertyWithMostRecentClaimAndIndicator = p.formatMostRecentProperty
return p