Модуль:WikidataSelectors

Для документации этого модуля может быть создана страница Модуль:WikidataSelectors/doc

local i18n = {    ["errors"] = {        ["rank-not-valid"] = "Некорретное значение приоритета (rank)",        ["cant-parse-condition"] = "Не удалось разобрать условие"    }}local validRanks = {'best','preferred','normal','deprecated'}--[[   Internal function for error message   Input: key in errors table  Output: error message]]local function throwError( key )    error( i18n.errors[key] )endlocal p = {}--[[   Main function for parse selectors and filter statements   Input: statements table, selector string  Output: filtered statements table]]function p.filter( allClaims, propertySelector )propertySelector = mw.text.trim( propertySelector )-- Get property ID from selectorlocal propertyId = mw.ustring.match( propertySelector, '^[Pp]%d+' )if not propertyId thenpropertyId = ''endlocal initPos = #propertyId + 1propertyId = string.upper( propertyId )    if ( not allClaims ) then    return nil    endlocal allPropertyClaims = allClaims[propertyId]    if ( not allPropertyClaims ) then    return nil    end-- Gathering ruleslocal rules = p.matchSelectors( propertySelector, initPos )-- If there is no rank filter, than default rank is 'best'local isRanked = falsefor i, subRules in ipairs( rules ) dofor j, rule in ipairs( subRules ) doif rule['type'] == 'rank' thenisRanked = truebreakendendendif not isRanked thentable.insert( rules, 1, { { type = 'rank', value = 'best' } } )end-- Execute rulesallPropertyClaims = p.applyRules( allPropertyClaims, rules )return allPropertyClaimsend--[[  Match and gather selector rules   Input: string with selectors rules, start position  Output: rules table]]function p.matchSelectors( selectorsString, initPos )local rules = {}local rawRulePattern = '^%s*%[%s*[^%[%]]+%s*%]%s*'local rulePattern = '^%s*%[%s*([^%[%]]+)%s*%]%s*$'if not initPos theninitPos = 1endlocal rawRule = mw.ustring.match( selectorsString, rawRulePattern, initPos )while rawRule doinitPos = initPos + #rawRulerule = mw.ustring.match( rawRule, rulePattern )rule = mw.text.trim( rule )local subRules = mw.text.split( rule, '%s*,%s*' )local commands = {}local commfor i, subRule in ipairs( subRules ) dolocal isInversed = falseif mw.ustring.match( subRule, '^!' ) thenisInversed = truesubRule = mw.ustring.match( subRule, '^!%s*(.+)$' )end-- p123[1]if mw.ustring.match( subRule, '^%d+$' ) thentable.insert( commands, {type = 'position',value = subRule,inversed = isInversed} )-- p123[rank:preferred]elseif mw.ustring.match( subRule, '^rank%s*:%s*(%a+)$' ) thenrank = mw.ustring.match( subRule, '^rank%s*:%s*(%a+)$' )table.insert( commands, {type = 'rank',value = rank,inversed = isInversed} )-- p123[language:xx]elseif mw.ustring.match( subRule, '^language%s*:%s*([%a%-]+)$' ) thenvalue = mw.ustring.match( subRule, '^language%s*:%s*([%a%-]+)$' )table.insert( commands, {type = 'language',value = value,inversed = isInversed} )-- p123[language!:xx]elseif mw.ustring.match( subRule, '^language%s*!:%s*([%a%-]+)$' ) thenvalue = mw.ustring.match( subRule, '^language%s*!:%s*([%a%-]+)$' )table.insert( commands, {type = 'language',value = value,inversed = not isInversed} )-- p123[min]elseif mw.ustring.match( subRule, '^min$' ) thentable.insert( commands, { type = 'value_min' } )-- p123[max]elseif mw.ustring.match( subRule, '^max$' ) thentable.insert( commands, { type = 'value_max' } )-- p123[min:p456]elseif mw.ustring.match( subRule, '^min%s*:%s*[Pp]%d+$' ) thenvalue = mw.ustring.match( subRule, ':%s*([Pp]%d+)$' )table.insert( commands, {type = 'qualifier_min',qualifier = value} )-- p123[max:p456]elseif mw.ustring.match( subRule, '^max%s*:%s*[Pp]%d+$' ) thenvalue = mw.ustring.match( subRule, ':%s*([Pp]%d+)$' )table.insert( commands, {type = 'qualifier_max',qualifier = value} )-- p123[unit:q789]elseif mw.ustring.match( subRule, '^unit%s*:%s*[^%[%],:]+$' ) thenvalue = mw.ustring.match( subRule, ':%s*([^%[%],:]+)$' )table.insert( commands, {type = 'unit',value = value,inversed = isInversed} )-- p123[unit!:q789]elseif mw.ustring.match( subRule, '^unit%s*!:%s*[^%[%],:]+$' ) thenvalue = mw.ustring.match( subRule, '!:%s*([^%[%],:]+)$' )table.insert( commands, {type = 'unit',value = value,inversed = not isInversed} )-- p123[p456]elseif mw.ustring.match( subRule, '^[Pp]%d+$' ) thenqualifier = mw.ustring.match( subRule, '^[Pp]%d+' )table.insert( commands, {type = 'qualifier',qualifier = qualifier,value = nil,inversed = isInversed} )-- p123[p456:q789]elseif mw.ustring.match( subRule, '^[Pp]%d+%s*:%s*[^%[%],:]+$' ) thenqualifier = mw.ustring.match( subRule, '^([Pp]%d+)%s*:?' )value = mw.ustring.match( subRule, ':%s*([^%[%],:]+)$' )table.insert( commands, {type = 'qualifier',qualifier = qualifier,value = value,inversed = isInversed} )-- p123[p456!:q789]elseif mw.ustring.match( subRule, '^[Pp]%d+%s*!:%s*[^%[%],:]+$' ) thenqualifier = mw.ustring.match( subRule, '^([Pp]%d+)%s*!:?' )value = mw.ustring.match( subRule, '!:%s*([^%[%],:]+)$' )table.insert( commands, {type = 'qualifier',qualifier = qualifier,value = value,inversed = not isInversed} )-- p123[q456]elseif mw.ustring.match( subRule, '^[Qq]%d+$' ) thenvalue = mw.ustring.match( subRule, '^[Qq]%d+' )table.insert( commands, {type = 'value',value = value,inversed = isInversed} )elsethrowError( 'cant-parse-condition' )endendif #commands thentable.insert( rules, commands )endrawRule = mw.ustring.match( selectorsString, rawRulePattern, initPos )endreturn rulesend--[[  Intercept statements with selector rules   Input: statements table, selector rules  Output: filtered statements table]]function p.applyRules( claims, rules )for i, subRules in ipairs( rules ) dolocal newClaims = {}for j, rule in ipairs( subRules ) doif rule['type'] == 'rank' thentable.insert( newClaims, p.filterByRank( claims, rule['value'], rule['inversed'] ) )elseif rule['type'] == 'language' thentable.insert( newClaims, p.filterByLanguage( claims, rule['value'], rule['inversed'] ) )elseif rule['type'] == 'unit' thentable.insert( newClaims, p.filterByUnit( claims, rule['value'], rule['inversed'] ) )elseif rule['type'] == 'position' thentable.insert( newClaims, p.filterByPosition( claims, rule['value'], rule['inversed'] ) )elseif rule['type'] == 'qualifier' thentable.insert( newClaims, p.filterByQualifier( claims, rule['qualifier'], rule['value'], rule['inversed'] ) )elseif rule['type'] == 'qualifier_min' thentable.insert( newClaims, p.filterUtterByQualifier( claims, rule['qualifier'], true ) )elseif rule['type'] == 'qualifier_max' thentable.insert( newClaims, p.filterUtterByQualifier( claims, rule['qualifier'], false ) )elseif rule['type'] == 'value' thentable.insert( newClaims, p.filterByValue( claims, rule['value'], rule['inversed'] ) )elseif rule['type'] == 'value_min' thentable.insert( newClaims, p.filterUtter( claims, true ) )elseif rule['type'] == 'value_max' thentable.insert( newClaims, p.filterUtter( claims, false ) )endendclaims = {}--[[Merge all claimsTODO: It's not good]]for j, newSubClaims in ipairs( newClaims ) dofor k, newClaim in ipairs( newSubClaims ) dolocal isNew = truefor l, oldClaim in ipairs( claims ) doif oldClaim['id'] == newClaim['id'] thenisNew = falsebreakendendif isNew thentable.insert( claims, newClaim )endendendendreturn claimsend--[[  Filter statements by rank   Input: claims table, rank value, inversion  Output: filtered statements table]]function p.filterByRank( claims, rank, inversed )if not inversed theninversed = falseendif not rank thenrank = 'best'end-- Check if rank value is validlocal isValidRank = falsefor i, validRank in ipairs( validRanks ) doif rank == validRank thenisValidRank = truebreakendendif not isValidRank thenthrowError( 'rank-not-valid' )end-- Find the best rankif rank == 'best' thenrank = 'normal' -- default rank (don't use deprecated even if it's no more claims)-- If we have at least one preferred rank, mark it as bestfor i, statement in pairs( claims ) doif (statement.rank == 'preferred') thenrank = 'preferred'breakendendendlocal resultClaims = {};for i, statement in pairs( claims ) doif ( statement.rank == rank ) ~= inversed thentable.insert( resultClaims, statement )endendreturn resultClaimsend--[[  Filter statements by language of value   Input: claims table, language, inversion  Output: filtered statements table]]function p.filterByLanguage( claims, language, inversed )if not inversed theninversed = falseendlocal resultClaims = {}local mulStatement = {}for i, statement in ipairs( claims ) doisMatchLanguage = falseif statement['mainsnak']and statement['mainsnak']['datavalue']and statement['mainsnak']['datavalue']['value']and statement['mainsnak']['datavalue']['value']['language'] thenif statement['mainsnak']['datavalue']['value']['language'] == language thenisMatchLanguage = trueendif statement['mainsnak']['datavalue']['value']['language'] == 'mul' thenmulStatement = statementendendif isMatchLanguage ~= inversed thentable.insert( resultClaims, statement )endendif next(resultClaims) == nil and next(mulStatement) ~= nil then-- if specific language is not found, but there is Q20923490 valuetable.insert( resultClaims, mulStatement )endreturn resultClaimsend--[[  Filter statements by unit of value   Input: claims table, unit, inversion  Output: filtered statements table]]function p.filterByUnit( claims, unit, inversed )if not inversed theninversed = falseendunit = 'http://www.wikidata.org/entity/' .. string.upper( unit )local resultClaims = {}for i, statement in ipairs( claims ) doisMatchUnit = falseif statement['mainsnak']and statement['mainsnak']['datavalue']and statement['mainsnak']['datavalue']['value']and statement['mainsnak']['datavalue']['value']['unit']and statement['mainsnak']['datavalue']['value']['unit'] == unit thenisMatchUnit = trueendif isMatchUnit ~= inversed thentable.insert( resultClaims, statement )breakendendreturn resultClaimsend--[[  Filter statements by position   Input: claims table, position, inversion  Output: filtered statements table]]function p.filterByPosition( claims, position, inversed )if not inversed theninversed = falseendlocal resultClaims = {};for statementPosition, statement in ipairs( claims ) doif ( statementPosition == tonumber( position ) ) ~= inversed thentable.insert( resultClaims, statement )breakendendreturn resultClaimsend--[[  Filter statements by qualifier existance or it's value   Input: claims table, ID of qualifier's property, qualifier's value, inversion  Output: filtered statements table]]function p.filterByQualifier( claims, qualifierId, value, inversed )if not inversed theninversed = falseendqualifierId = string.upper( qualifierId )local resultClaims = {}for i, statement in ipairs( claims ) doif statement['qualifiers'] and statement['qualifiers'][qualifierId] thenif value == nil thenif ( #statement['qualifiers'][qualifierId] > 0 ) ~= inversed thentable.insert( resultClaims, statement )endelselocal isQualifierFound = falsefor j, qualifier in ipairs( statement['qualifiers'][qualifierId] ) doif qualifier['datavalue'] thenlocal qualifierValue = qualifier['datavalue']['value']if qualifier['datavalue']['type'] == 'wikibase-entityid' thenqualifierValue = qualifierValue.idvalue = string.upper( value )endif qualifierValue == value thenisQualifierFound = truebreakendendendif isQualifierFound ~= inversed thentable.insert( resultClaims, statement )endendelseif inversed thentable.insert( resultClaims, statement )endendreturn resultClaimsend--[[  Filter statements by it's values   Input: claims table, value, inversed  Output: filtered statements table]]function p.filterByValue( claims, value, inversed )inversed = inversed or falselocal resultClaims = {}for i, statement in ipairs( claims ) dolocal statementValueif statement['mainsnak'] and statement['mainsnak']['datavalue'] and statement['mainsnak']['datavalue']['type']then statementValue = statement['mainsnak']['datavalue']['value']if statement['mainsnak']['datavalue']['type'] == 'quantity' thenstatementValue = statementValue.amountendif statement['mainsnak']['datavalue']['type'] == 'time' thenstatementValue = statementValue.timeendif statement['mainsnak']['datavalue']['type'] == 'wikibase-entityid' thenstatementValue = statementValue.idvalue = string.upper( value )endendif ( statementValue == value ) ~= inversed thentable.insert( resultClaims, statement )endendreturn resultClaimsend--[[  Find a statement with minimum or maximum value   Input: claims table, asc, inversed  Output: filtered statements table]]function p.filterUtter( claims, asc, inversed )local resultValue = nilfor i, statement in ipairs( claims ) dolocal statementValueif statement['mainsnak'] andstatement['mainsnak']['datavalue'] andstatement['mainsnak']['datavalue']['type']thenstatementValue = statement['mainsnak']['datavalue']['value']if statement['mainsnak']['datavalue']['type'] == 'quantity' thenstatementValue = statementValue.amountendif statement['mainsnak']['datavalue']['type'] == 'time' thenstatementValue = statementValue.timeendif statement['mainsnak']['datavalue']['type'] == 'wikibase-entityid' thenstatementValue = statementValue.idendif not resultValue or ( statementValue < resultValue ) == asc thenresultValue = statementValueendendendmw.logObject( resultValue, 'resultValue' )return p.filterByValue( claims, resultValue, inversed )end--[[  Find a statement with minimum or maximum qualifier value   Input: claims table, qualifierId, asc  Output: filtered statements table]]function p.filterUtterByQualifier( claims, qualifierId, asc )qualifierId = string.upper( qualifierId )local resultValue = nillocal resultStatement = nilfor i, statement in ipairs( claims ) doif not statement['qualifiers'] and not statement['qualifiers'][qualifierId] thenif resultStatement == nil thenresultStatement = statementendelsefor _, qualifier in ipairs( statement['qualifiers'][qualifierId] ) doif qualifier['datavalue'] thenlocal qualifierValue = qualifier['datavalue']['value']if qualifier['datavalue']['type'] == 'quantity' thenqualifierValue = qualifierValue.amountendif qualifier['datavalue']['type'] == 'time' thenqualifierValue = qualifierValue.timeendif qualifier['datavalue']['type'] == 'wikibase-entityid' thenqualifierValue = qualifierValue.idendif not resultValue or ( qualifierValue < resultValue ) == asc thenresultStatement = statementresultValue = qualifierValueendendendendendreturn { resultStatement }endreturn p