Модуль:Wikidata/Population
Перейти к навигации
Перейти к поиску
Модуль для шаблонов {{Wikidata/Population}} и {{Wikidata/Population/Table}}.
local WDS = require('Module:WikidataSelectors');
local p = {};
local DEFAULT_WIDTH = 500;
local DEFAULT_HEIGHT = 250;
local COLLAPSE_IF_MORE_THAN = 30;
local TABLE_COLLAPSIBLE_HEADER = "Статистика численности населения с %s по %s";
local TABLE_COLUMN_HEADER_YEAR = "Год";
local TABLE_COLUMN_HEADER_POPULATION = "Численность";
local function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
local function formatPopulationPropertyImpl( context, options )
if ( not context ) then error( 'context not specified' ); end;
if ( not options ) then error( 'options not specified' ); end;
if ( not options.entity ) then error( 'options.entity missing' ); end;
local claims = context.selectClaims( options, options.property );
if (claims == nil) then
return nil -- TODO error?
end
local validClaims = {}
for _, claim in ipairs( claims ) do
-- проверка на наличие корректного момента времени
if claim.qualifiers.P585[1] and claim.qualifiers.P585[1].snaktype == 'value' then
table.insert( validClaims, claim )
end
end
if #validClaims == 0 then
return nil -- TODO error?
end
local comparator = function( o1, o2 )
local t1 = context.parseTimeFromSnak( o1.qualifiers.P585[1] )
local t2 = context.parseTimeFromSnak( o2.qualifiers.P585[1] )
return t1 < t2
end
table.sort( validClaims, comparator )
return validClaims
end
-- тестирование не работает
-- =p.formatProperty(mw.getCurrentFrame():newChild{title="Модуль:Wikidata",args={["property-module"]="Wikidata/Population",["property-function"]="formatPopulationPropertyForGraph",["claim-module"]="Wikidata/Population",["claim-function"]="formatPopulationClaimForGraph",["property"]="p1082[p585][rank:preferred,rank:normal]",["datatype"]="quantity",}}:newChild{title="Модуль:Wikidata/Population"}:newChild{title="Сереседа-де-ла-Сьерра"})
function p.formatPopulationPropertyForGraph( context, options )
local claims = formatPopulationPropertyImpl( context, options );
-- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных
-- заявлений в таблице
local formattedClaims = {}
local years = {}
local count = 0;
local csv = 'year,month,day,population,formatted';
if ( not claims ) then
return '';
end
for i, claim in ipairs(claims) do
-- уточняем даты: для года до середины, для месяца до 15-го числа
local p585Value = claim.qualifiers.P585[1].datavalue.value;
local p585Precision = p585Value.precision;
local p585Time = p585Value.time;
if ( p585Precision == 10 ) then
-- Set 15-th day of month
p585Time = mw.ustring.gsub(p585Time, "-[0-9]+T", "-15T");
elseif ( p585Precision == 9 ) then
-- Set to 1-st of July
p585Time = mw.ustring.gsub(p585Time, "-[0-9]+-[0-9]+T", "-07-01T");
end
local year, month, day = mw.ustring.gmatch( p585Time, "(-?[0-9]+)-([0-9]+)-([0-9]+)T" )(1);
if claim.mainsnak.datavalue and not years[ year ] then
years[ year ] = true;
local value = string.gsub( claim.mainsnak.datavalue.value.amount, '^%+', '' )
local formatted = mw.language.getContentLanguage():formatNum( tonumber( value ) );
local line = year .. ',' .. month .. ',' .. day .. ',' .. value .. ',' .. formatted;
csv = csv .. '\\n' .. line;
count = count + 1;
end
end
if ( count == 0 ) then
return '';
end
local graphData = [[{
"version": 2,
"width": ]] .. DEFAULT_WIDTH .. [[,
"height": ]] .. DEFAULT_HEIGHT .. [[,
"data": [ {
"name": "table",
"values": "]] .. csv .. [[",
"format": {
"parse": {
"year": "integer",
"month": "integer",
"day": "integer",
"population": "integer",
"formatted": "string"
},
"type": "csv"
},
"transform": [
{
"type": "formula",
"field": "date",
"expr": "datetime(datum.year,datum.month-1,datum.day)"
}
]
} ],
"scales": [
{
"name": "x",
"type": "time",
"range": "width",
"nice": "year",
"domain": { "data": "table", "field": "date" }
},
{
"name": "y",
"type": "linear",
"range": "height",
"domain": { "data": "table", "field": "population" }
}
],
"axes": [
{ "type": "x", "scale": "x", "ticks": 10 },
{ "type": "y", "scale": "y", "ticks": 5, "grid": true, "orient": "right", "format": "d" }
],
"marks": [
{
"type": "area",
"from": { "data": "table" },
"properties": {
"enter": {
"x": { "scale": "x", "field": "date" },
"y": { "scale": "y", "value": 0 },
"y2": { "scale": "y", "field": "population" },
"fill": { "value": "#99B2CC" },
"fillOpacity": { "value": 0.35 },
"interpolate": { "value": "linear" }
}
}
},
{
"type": "line",
"from": { "data": "table" },
"properties": {
"enter": {
"x": { "scale": "x", "field": "date" },
"y": { "scale": "y", "field": "population" },
"stroke": { "value": "#99B2CC" },
"strokeWidth": { "value": 3 },
"interpolate": { "value": "linear" }
}
}
},
{
"type": "symbol",
"from": { "data": "table" },
"properties": {
"enter": {
"x": { "scale": "x", "field": "date" },
"y": { "scale": "y", "field": "population" },
"stroke": { "value": "#99B2CC" },
"fill": { "value": "#fff" },
"size": { "value": 10 }
}
}
},
{
"type": "text",
"from": { "data": "table" },
"properties": {
"enter": {
"x": { "scale": "x", "field": "date", "offset": -5 },
"y": { "scale": "y", "field": "population", "offset": -1 },
"align": { "value": "left" },
"opacity": { "value": "0" },
"fill": { "value": "#000000" },
"size": { "value": 4 },
"text": { "template": "{{datum.formatted}} ({{datum.year}})" }
},
"hover": {
"opacity": { "value": "1" }
},
"update": {
"opacity": { "value": "0" }
}
}
}
]
}]]
local result = options.frame:callParserFunction{ name = '#tag:graph', args = { graphData, mode = 'interactive' } };
if count > COLLAPSE_IF_MORE_THAN then
return result;
else
-- side-by-side display
return '<div style="vertical-align: bottom;">' .. result .. '</div>';
end
end
function p.formatPopulationClaimForGraph( context, options, statement )
local time = context.parseTimeFromSnak( statement.qualifiers.P585[1] );
local value = string.gsub( statement.mainsnak.datavalue.value.amount, '^%+', '' );
return os.date("*t", time / 1000).year .. ',' .. value;
end
function p.formatPopulationPropertyForTable( context, options )
local claims = formatPopulationPropertyImpl( context, options );
-- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных
-- заявлений в таблице
local formattedClaims = {}
local years = {}
local firstTime = false;
local lastTime = '';
local count = 0;
if ( not claims ) then
return '';
end
if options.nolinks == nil then
options.nolinks = true
end
for i, claim in ipairs( claims ) do
-- обрезаем выводимую дату до года
local timeQualifier = claim.qualifiers.P585[1];
if ( timeQualifier.datavalue.value.precision > 9 ) then
timeQualifier = deepcopy( timeQualifier );
timeQualifier.datavalue.value.precision = 9;
end
local year = string.sub( timeQualifier.datavalue.value.time, 2, 5 );
if claim.mainsnak.datavalue and not years[ year ] then
years[ year ] = true
local time = context.formatSnak( options, timeQualifier )
local value = context.formatSnak( options, claim.mainsnak )
if not firstTime then
firstTime = time
end
lastTime = time
-- link to topic (census)
if claim.qualifiers.P805 and
claim.qualifiers.P805[1] and
claim.qualifiers.P805[1].snaktype == 'value'
then
local link = mw.wikibase.sitelink( claim.qualifiers.P805[1].datavalue.value.id )
if link then
time = '[[' .. link .. '|' .. time .. ']]'
end
end
local line = '\n|- role="row"'
line = line .. '\n! role="rowheader" scope="row" | ' .. time
line = line .. '\n| role="cell" | ' .. ( value or "" )
line = line .. '\n| role="cell" | '
if options.references then
line = line .. context.formatRefs( options, claim )
end
table.insert( formattedClaims, line )
count = count + 1
end
end
if ( count == 0 ) then
return '';
end
local out = '<div class="ts-wikidata-population-table'
if count < 20 then
out = out .. ' cols-' .. ( math.floor( count / 5 ) + 1 )
end
out = out .. '">\n'
local caption = mw.ustring.format( TABLE_COLLAPSIBLE_HEADER, firstTime, lastTime );
out = out .. '{| role="table" class="wikitable mw-collapsible'
if count > COLLAPSE_IF_MORE_THAN then
out = out .. ' mw-collapsed'
end
out = out .. '"\n|+ ' .. caption
out = out .. '\n|- role="row"'
out = out .. '\n! role="columnheader" aria-sort="ascending" scope="col" | ' .. TABLE_COLUMN_HEADER_YEAR
out = out .. '\n! role="columnheader" aria-sort="none" scope="colgroup" colspan="2" | ' .. TABLE_COLUMN_HEADER_POPULATION
out = out .. '\n|-'
out = out .. table.concat( formattedClaims, '\n' )
out = out .. '\n|}';
out = out .. '\n</div>';
return out
end
return p;