Модуль:Wikidata/Population
Модуль для шаблонов {{Wikidata/Population}} и {{Wikidata/Population/Table}}.
local WDS = require('Module:WikidataSelectors');
local p = {};
local DEFAULT_COLUMNS = 4;
local DEFAULT_WIDTH = 700;
local DEFAULT_HEIGHT = 300;
local COLLAPSE_IF_ROWS_MORE_THAN = 11;
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
for i, j in ipairs(claims) do
if ( not j.qualifiers.P585[1]) then return nil end --проверка на наличие момента времени
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( claims, comparator )
return claims;
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 not years[ year ] then
years[ year ] = true;
local value
if not claim.mainsnak.datavalue then value = ""
else value = string.gsub( claim.mainsnak.datavalue.value.amount, '^%+', '' ) end
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": "';
graphData = graphData .. csv;
graphData = graphData .. '","format": { "parse": {"year": "integer", "month": "integer", "day": "integer", "population": "integer", "formatted": "string"}, "type": "csv" },'
graphData = graphData .. '"transform": [{ "type": "formula", "field": "date", "expr": "datetime(datum.year,datum.month-1,datum.day)" }] } ],';
graphData = graphData .. '"scales": [{ "name": "x", "type": "time", "range": "width", "nice": "year", "domain": {"data": "table", "field": "date"} },';
graphData = graphData .. '{ "name": "y", "type": "linear", "range": "height", "domain": {"data": "table", "field": "population"} } ],';
graphData = graphData .. '"axes": [ {"type": "x", "scale": "x", "ticks": 10}, {"type": "y", "scale": "y", "ticks": 5, "grid": true, "orient": "right", "format": "d"} ],';
graphData = graphData .. '"marks": [{ "type": "area", "from": {"data": "table"}, "properties": { "enter": {';
graphData = graphData .. '"x": {"scale": "x", "field": "date"}, "y": {"scale": "y", "value": 0}, "y2": {"scale": "y", "field": "population"}, ';
graphData = graphData .. '"fill": {"value": "#99B2CC"}, "fillOpacity": {"value": 0.35}, "interpolate": {"value": "linear"}}}},';
graphData = graphData .. '{ "type": "line", "from": {"data": "table"}, "properties": { "enter": {';
graphData = graphData .. '"x": {"scale": "x", "field": "date"}, "y": {"scale": "y", "field": "population"},';
graphData = graphData .. '"stroke": {"value": "#99B2CC"}, "strokeWidth": {"value": 3}, "interpolate": {"value": "linear"}}}},';
graphData = graphData .. '{"type": "symbol","from": {"data": "table"},"properties": {"enter": {';
graphData = graphData .. '"x": {"scale": "x", "field": "date"},"y": {"scale": "y", "field": "population"},"stroke": {"value": "#99B2CC"},"fill": {"value": "#fff"},"size": {"value": 10}}}},'
graphData = graphData .. '{"type": "text", "from": {"data": "table"}, "properties": { "enter": { "x": {"scale": "x", "field": "date"}, "y": {"scale": "y", "field": "population", "offset": -1},';
graphData = graphData .. '"align": {"value": "center"}, "opacity": {"value": "0"}, "fill": {"value": "#000000"}, "size": {"value": 4}, "text": {"template": "{{datum.formatted}} ({{datum.year}})"}},';
graphData = graphData .. '"hover": {"opacity": {"value": "1"}}, "update": {"opacity": {"value": "0"}}}},';
graphData = graphData .. ']}';
local result = options.frame:callParserFunction{ name = '#tag:graph', args = { graphData, mode = 'interactive' } };
local columns = options.columns or DEFAULT_COLUMNS;
local perColumn = math.ceil( count / columns );
if ( perColumn > COLLAPSE_IF_ROWS_MORE_THAN ) then
return result;
else
-- side-by-side display
return '<div style="display: inline-block; 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
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 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;
local line = '\n|-\n! scope="row" | ' .. time
line = line .. '\n| style="text-align: right; border-right: none; padding-right: 0;" | ' .. ( value or "" );
line = line .. '\n| style="text-align: left; border-left: none; padding-left: 0;" | '
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 columns = options.columns or DEFAULT_COLUMNS;
if ( count <= columns ) then
local out = '{| class="wikitable" \n|-\n! scope="col" | ' .. TABLE_COLUMN_HEADER_YEAR .. ' !! scope="colgroup" colspan="2" | ' .. TABLE_COLUMN_HEADER_POPULATION .. '\n|-';
for i, formattedClaim in ipairs(formattedClaims) do
out = out .. '\n' .. formattedClaim;
end
out = out .. '\n|}';
return out;
end
local out = '';
local perColumn = math.ceil( count / columns );
if ( perColumn > COLLAPSE_IF_ROWS_MORE_THAN ) then
local caption = mw.ustring.format( TABLE_COLLAPSIBLE_HEADER, firstTime, lastTime );
out = out .. '{| class="collapsible collapsed" |\n';
out = out .. '! ' .. caption .. '\n';
out = out .. '|-\n';
out = out .. '| class="ts-wikidata-population-table" |';
else
out = out .. '<div class="ts-wikidata-population-table">';
end
for i, formattedClaim in ipairs(formattedClaims) do
if ( i % perColumn == 1 ) then
out = out .. '\n{| class="wikitable" \n|-\n! scope="col" | ' .. TABLE_COLUMN_HEADER_YEAR .. ' !! scope="colgroup" colspan=2 | ' .. TABLE_COLUMN_HEADER_POPULATION .. '\n|-';
end
out = out .. '\n' .. formattedClaim;
if ( i % perColumn == 0 ) then
out = out .. '\n|}';
end
end
if ( count % perColumn ~= 0 ) then
out = out .. '\n|}';
end
if ( perColumn > COLLAPSE_IF_ROWS_MORE_THAN ) then
out = out .. '\n|}';
else
out = out .. '\n</div>';
end
return out
end
return p;