Last edited 4 months ago
by Meryam Sahin

Module:NeoWikiRollup: Difference between revisions

Meryam Sahin (talk | contribs)
No edit summary
Meryam Sahin (talk | contribs)
No edit summary
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
local p = {}
local p = {}
local nw = require( 'mw.neowiki' )


function p.value( frame )
local nw = require('mw.neowiki')
local property = frame.args[1]
local page = frame.args['page']


local options = nil
function p.value(frame)
if page then
local property = frame.args[1]
options = { page = page }
local page = frame.args['page']
end


local v = nw.getValue( property, options )
local options = nil
if page then options = { page = page } end


return tostring( v or '' )
local v = nw.getValue(property, options)
return tostring(v or '')
end
end


function p.values( frame )
function p.values(frame)
local property = frame.args[1]
local property = frame.args[1]
local page = frame.args['page']
local page = frame.args['page']
local separator = frame.args['separator'] or ', '
local separator = frame.args['separator'] or ', '


local options = nil
local options = nil
if page then
if page then options = { page = page } end
options = { page = page }
end


local all = nw.getAll( property, options )
local all = nw.getAll(property, options)
if not all then return '' end


if not all then
local parts = {}
return ''
for _, item in ipairs(all) do
end
  parts[#parts + 1] = tostring(item)
end


local parts = {}
return table.concat(parts, separator)
for _, item in ipairs( all ) do
parts[#parts + 1] = tostring( item )
end
 
return table.concat( parts, separator )
end
end


function p.subject( frame )
function p.subject(frame)
local page = frame.args[1]
local page = frame.args[1]
local s = nw.getMainSubject( page )
local s = nw.getMainSubject(page)
 
if not s then return 'No subject found' end
if not s then
return 'No subject found'
end


local rows = {}
local rows = {}
rows[#rows + 1] = '{| class="wikitable"'
rows[#rows + 1] = '{| class="table table-striped"'
rows[#rows + 1] = '! Property !! Type !! Value(s)'
rows[#rows + 1] = '! Property !! Type !! Value(s)'


for name, stmt in pairs( s.statements ) do
for name, stmt in pairs(s.statements) do
local vals = {}
  local vals = {}
for _, v in ipairs( stmt.values ) do
  for _, v in ipairs(stmt.values) do
if type( v ) == 'table' then
  if type(v) == 'table' then
vals[#vals + 1] = v.label or v.target or tostring( v )
    vals[#vals + 1] = v.label or v.target or tostring(v)
else
  else
vals[#vals + 1] = tostring( v )
    vals[#vals + 1] = tostring(v)
end
  end
end
  end
rows[#rows + 1] = '|-'
  rows[#rows + 1] = '|-'
rows[#rows + 1] = '| ' .. name .. ' || ' .. stmt.type .. ' || ' .. table.concat( vals, ', ' )
  rows[#rows + 1] = '| ' .. name .. ' || ' .. stmt.type .. ' || ' .. table.concat(vals, ', ')
end
end


rows[#rows + 1] = '|}'
rows[#rows + 1] = '|}'
return table.concat( rows, '\n' )
return table.concat(rows, '\n')
end
end


function p.children( frame )
function p.children(frame)
local page = frame.args[1]
local page = frame.args[1]
local children = nw.getChildSubjects( page )
local children = nw.getChildSubjects(page)


if not children or #children == 0 then
if not children or #children == 0 then return 'No child subjects' end
return 'No child subjects'
end


local parts = {}
local parts = {}
for _, child in ipairs( children ) do
for _, child in ipairs(children) do
parts[#parts + 1] = "'''" .. child.label .. "''' (" .. child.schema .. ")"
  parts[#parts + 1] = "'''" .. child.label .. "''' (" .. child.schema .. ")"
end
end


return table.concat( parts, ', ' )
return table.concat(parts, ', ')
end
end


local function renderRowsAsTable( rows, columns, linkColumns, linkNamespace )
local function renderRowsAsTable(rows, columns, linkColumns, linkNamespace, labelMode)
if #rows == 0 then
if #rows == 0 then return 'No results' end
return 'No results'
 
end
if not columns then
  columns = {}
  for k in pairs(rows[1]) do columns[#columns + 1] = k end
  table.sort(columns)
end
 
local linkSet = {}
if linkColumns then
  for _, col in ipairs(linkColumns) do linkSet[col] = true end
end


if not columns then
local out = { '{| class="table table-striped"', '! ' .. table.concat(columns, ' !! ') }
columns = {}
for k in pairs( rows[1] ) do
columns[#columns + 1] = k
end
table.sort( columns )
end


local linkSet = {}
for _, row in ipairs(rows) do
if linkColumns then
  local cells = {}
for _, col in ipairs( linkColumns ) do
linkSet[col] = true
end
end


local out = { '{| class="wikitable"', '! ' .. table.concat( columns, ' !! ' ) }
  for _, col in ipairs(columns) do
  local v = row[col]
  local cell = v == nil and '' or tostring(v)


for _, row in ipairs( rows ) do
  if linkSet[col] and cell ~= '' then
local cells = {}
for _, col in ipairs( columns ) do
local v = row[col]
local cell = v == nil and '' or tostring( v )
if linkSet[col] and cell ~= '' then
     local ns = linkNamespace and linkNamespace[col]
     local ns = linkNamespace and linkNamespace[col]


     local fullTitle
     local fullTitle
     if ns and ns ~= '' then
     if ns and ns ~= '' then
        fullTitle = ns .. ':' .. cell
    fullTitle = ns .. ':' .. cell
     else
     else
        fullTitle = cell
    fullTitle = cell
     end
     end


     local titleObj = mw.title.new(fullTitle)
     local titleObj = mw.title.new(fullTitle)


     local label = cell
     local label
     if titleObj and titleObj.subpageText ~= '' then
    if labelMode == 'full' then
        label = titleObj.subpageText
    label = titleObj and titleObj.text or cell
     else
    label = (titleObj and titleObj.subpageText ~= '') and titleObj.subpageText or (titleObj and titleObj.text or cell)
     end
     end


     cell = '[[' .. fullTitle .. '|' .. label .. ']]'
     cell = '[[' .. fullTitle .. '|' .. label .. ']]'
end
  end
 
  cells[#cells + 1] = cell
  end


cells[#cells + 1] = cell
  out[#out + 1] = '|-'
end
  out[#out + 1] = '| ' .. table.concat(cells, ' || ')
out[#out + 1] = '|-'
end
out[#out + 1] = '| ' .. table.concat( cells, ' || ' )
end


out[#out + 1] = '|}'
out[#out + 1] = '|}'
return table.concat( out, '\n' )
return table.concat(out, '\n')
end
end


function p.query( frame )
function p.query(frame)
local columns = nil
if frame.args.columns then
columns = mw.text.split( frame.args.columns, ',%s*' )
end
 
local linkColumns = nil
if frame.args.linkColumns then
linkColumns = mw.text.split( frame.args.linkColumns, ',%s*' )
end
 
local linkNamespace = {}


for k,v in pairs(frame.args) do
local columns = nil
    if type(k) == 'string' then
if frame.args.columns then
        local col = k:match('^linkNamespace(.+)$')
  columns = mw.text.split(frame.args.columns, ',%s*')
        if col then
end
            linkNamespace[mw.text.trim(col)] = mw.text.trim(v)
        end
    end
end


local linkColumns = nil
if frame.args.linkColumns then
  linkColumns = mw.text.split(frame.args.linkColumns, ',%s*')
end


local linkNamespace = {}
for k, v in pairs(frame.args) do
  if type(k) == 'string' then
  local col = k:match('^linkNamespace(.+)$')
  if col then
    linkNamespace[mw.text.trim(col)] = mw.text.trim(v)
  end
  end
end


local labelMode = frame.args.linkLabel or 'subpage'


    return renderRowsAsTable(
return renderRowsAsTable(
        nw.query( frame.args[1] ),
  nw.query(frame.args[1]),
        columns,
  columns,
        linkColumns,
  linkColumns,
        linkNamespace  
  linkNamespace,
    )
  labelMode
)
end
end


 
local function statementValue(stmt)
local function statementValue( stmt )
if not stmt or not stmt.values or stmt.values[1] == nil then return nil end
if not stmt or not stmt.values or stmt.values[1] == nil then
local v = stmt.values[1]
return nil
if type(v) == 'table' then return v.label or v.target end
end
return v
local v = stmt.values[1]
if type( v ) == 'table' then
return v.label or v.target
end
return v
end
end


-- Renders a wikitable from the current page's child Subjects.
function p.childTable(frame)
-- Args: columns=Col1, Col2 (required, in order)
local columns = mw.text.split(frame.args.columns or '', ',%s*')
--      schema=SchemaName (optional, filters children to one schema)
local schemaFilter = frame.args.schema
--      sortBy=ColName (optional)
local sortBy = frame.args.sortBy
--      sortDir=asc|desc (optional, default desc)
local sortDir = frame.args.sortDir or 'desc'
--      numberColumns=Col1, Col2 (optional, formatted with thousand separators)
function p.childTable( frame )
local columns = mw.text.split( frame.args.columns or '', ',%s*' )
local schemaFilter = frame.args.schema
local sortBy = frame.args.sortBy
local sortDir = frame.args.sortDir or 'desc'


local numberSet = {}
local numberSet = {}
if frame.args.numberColumns then
if frame.args.numberColumns then
for _, col in ipairs( mw.text.split( frame.args.numberColumns, ',%s*' ) ) do
  for _, col in ipairs(mw.text.split(frame.args.numberColumns, ',%s*')) do
numberSet[col] = true
  numberSet[col] = true
end
  end
end
end


local children = nw.getChildSubjects()
local children = nw.getChildSubjects()
if not children then
if not children then return '' end
return ''
end


local lang = mw.getContentLanguage()
local lang = mw.getContentLanguage()
local rows = {}
local rows = {}


for _, child in ipairs( children ) do
for _, child in ipairs(children) do
if not schemaFilter or child.schema == schemaFilter then
  if not schemaFilter or child.schema == schemaFilter then
local row = {}
  local row = {}
for _, col in ipairs( columns ) do
  for _, col in ipairs(columns) do
local v = statementValue( child.statements[col] )
    local v = statementValue(child.statements[col])
if v == nil then
    if v == nil then
row[col] = ''
    row[col] = ''
elseif numberSet[col] and tonumber( v ) then
    elseif numberSet[col] and tonumber(v) then
row[col] = lang:formatNum( tonumber( v ) )
    row[col] = lang:formatNum(tonumber(v))
else
    else
row[col] = tostring( v )
    row[col] = tostring(v)
end
    end
end
  end
-- Stash the raw sort value so number columns sort numerically
  if sortBy then
-- even after thousand-separator formatting has stringified them.
    row.__sortValue = statementValue(child.statements[sortBy])
if sortBy then
  end
row.__sortValue = statementValue( child.statements[sortBy] )
  rows[#rows + 1] = row
end
  end
rows[#rows + 1] = row
end
end
end


if sortBy then
if sortBy then
table.sort( rows, function( a, b )
  table.sort(rows, function(a,b)
local av, bv = a.__sortValue, b.__sortValue
  local av,bv = a.__sortValue, b.__sortValue
local an, bn = tonumber( av ), tonumber( bv )
  local an,bn = tonumber(av), tonumber(bv)
if an and bn then
  if an and bn then
if sortDir == 'asc' then return an < bn end
    if sortDir == 'asc' then return an < bn end
return an > bn
    return an > bn
end
  end
if sortDir == 'asc' then return tostring( av ) < tostring( bv ) end
  if sortDir == 'asc' then return tostring(av) < tostring(bv) end
return tostring( av ) > tostring( bv )
  return tostring(av) > tostring(bv)
end )
  end)
end
end


return renderRowsAsTable( rows, columns, nil )
return renderRowsAsTable(rows, columns, nil)
end
end


function p.productsFoundedSince( frame )
function p.productsFoundedSince(frame)
local year = tonumber( frame.args[1] ) or 2000
local year = tonumber(frame.args[1]) or 2000
 
return renderRowsAsTable(nw.query(
return renderRowsAsTable( nw.query(
  'MATCH (n:Product) WHERE n.`Available since` >= $year RETURN n.name AS name, n.`Available since` AS year ORDER BY year',
'MATCH (n:Product) WHERE n.`Available since` >= $year ' ..
  { year = year }
'RETURN n.name AS name, n.`Available since` AS year ORDER BY year',
))
{ year = year }
) )
end
end


local function propertyDetails( prop )
local function propertyDetails(prop)
local details = {}
local details = {}


if prop.type == 'select' then
if prop.type == 'select' then
local labels = {}
  local labels = {}
for _, option in ipairs( prop.options ) do
  for _, option in ipairs(prop.options) do
labels[#labels + 1] = option.label
  labels[#labels + 1] = option.label
end
  end
details[#details + 1] = 'options: ' .. table.concat( labels, ', ' )
  details[#details + 1] = 'options: ' .. table.concat(labels, ', ')
elseif prop.type == 'number' then
elseif prop.type == 'number' then
if prop.minimum ~= nil then
  if prop.minimum ~= nil then details[#details + 1] = 'min: ' .. tostring(prop.minimum) end
details[#details + 1] = 'min: ' .. tostring( prop.minimum )
  if prop.maximum ~= nil then details[#details + 1] = 'max: ' .. tostring(prop.maximum) end
end
  if prop.precision ~= nil then details[#details + 1] = 'precision: ' .. tostring(prop.precision) end
if prop.maximum ~= nil then
elseif prop.type == 'relation' then
details[#details + 1] = 'max: ' .. tostring( prop.maximum )
  details[#details + 1] = 'targetSchema: ' .. prop.targetSchema
end
  details[#details + 1] = 'relation: ' .. prop.relation
if prop.precision ~= nil then
elseif prop.type == 'text' or prop.type == 'url' then
details[#details + 1] = 'precision: ' .. tostring( prop.precision )
  if prop.multiple then details[#details + 1] = 'multiple: true' end
end
  if prop.uniqueItems then details[#details + 1] = 'uniqueItems: true' end
elseif prop.type == 'relation' then
end
details[#details + 1] = 'targetSchema: ' .. prop.targetSchema
details[#details + 1] = 'relation: ' .. prop.relation
elseif prop.type == 'text' or prop.type == 'url' then
if prop.multiple then
details[#details + 1] = 'multiple: true'
end
if prop.uniqueItems then
details[#details + 1] = 'uniqueItems: true'
end
end


return table.concat( details, ', ' )
return table.concat(details, ', ')
end
end


function p.schema( frame )
function p.schema(frame)
local schema = nw.getSchema( frame.args[1] )
local schema = nw.getSchema(frame.args[1])
 
if not schema then return 'Schema not found' end
if not schema then
return 'Schema not found'
end


local rows = {}
local rows = {}
for _, prop in ipairs( schema.properties ) do
for _, prop in ipairs(schema.properties) do
rows[#rows + 1] = {
  rows[#rows + 1] = {
Name = prop.name,
  Name = prop.name,
Type = prop.type,
  Type = prop.type,
Required = prop.required and 'Yes' or 'No',
  Required = prop.required and 'Yes' or 'No',
Details = propertyDetails( prop ),
  Details = propertyDetails(prop)
}
  }
end
end


return renderRowsAsTable( rows, { 'Name', 'Type', 'Required', 'Details' } )
return renderRowsAsTable(rows, { 'Name', 'Type', 'Required', 'Details' })
end
end


return p
return p

Latest revision as of 11:20, 18 June 2026

Documentation for this module may be created at Module:NeoWikiRollup/doc

local p = {}

local nw = require('mw.neowiki')

function p.value(frame)
 local property = frame.args[1]
 local page = frame.args['page']

 local options = nil
 if page then options = { page = page } end

 local v = nw.getValue(property, options)
 return tostring(v or '')
end

function p.values(frame)
 local property = frame.args[1]
 local page = frame.args['page']
 local separator = frame.args['separator'] or ', '

 local options = nil
 if page then options = { page = page } end

 local all = nw.getAll(property, options)
 if not all then return '' end

 local parts = {}
 for _, item in ipairs(all) do
  parts[#parts + 1] = tostring(item)
 end

 return table.concat(parts, separator)
end

function p.subject(frame)
 local page = frame.args[1]
 local s = nw.getMainSubject(page)
 if not s then return 'No subject found' end

 local rows = {}
 rows[#rows + 1] = '{| class="table table-striped"'
 rows[#rows + 1] = '! Property !! Type !! Value(s)'

 for name, stmt in pairs(s.statements) do
  local vals = {}
  for _, v in ipairs(stmt.values) do
   if type(v) == 'table' then
    vals[#vals + 1] = v.label or v.target or tostring(v)
   else
    vals[#vals + 1] = tostring(v)
   end
  end
  rows[#rows + 1] = '|-'
  rows[#rows + 1] = '| ' .. name .. ' || ' .. stmt.type .. ' || ' .. table.concat(vals, ', ')
 end

 rows[#rows + 1] = '|}'
 return table.concat(rows, '\n')
end

function p.children(frame)
 local page = frame.args[1]
 local children = nw.getChildSubjects(page)

 if not children or #children == 0 then return 'No child subjects' end

 local parts = {}
 for _, child in ipairs(children) do
  parts[#parts + 1] = "'''" .. child.label .. "''' (" .. child.schema .. ")"
 end

 return table.concat(parts, ', ')
end

local function renderRowsAsTable(rows, columns, linkColumns, linkNamespace, labelMode)
 if #rows == 0 then return 'No results' end

 if not columns then
  columns = {}
  for k in pairs(rows[1]) do columns[#columns + 1] = k end
  table.sort(columns)
 end

 local linkSet = {}
 if linkColumns then
  for _, col in ipairs(linkColumns) do linkSet[col] = true end
 end

 local out = { '{| class="table table-striped"', '! ' .. table.concat(columns, ' !! ') }

 for _, row in ipairs(rows) do
  local cells = {}

  for _, col in ipairs(columns) do
   local v = row[col]
   local cell = v == nil and '' or tostring(v)

   if linkSet[col] and cell ~= '' then
    local ns = linkNamespace and linkNamespace[col]

    local fullTitle
    if ns and ns ~= '' then
     fullTitle = ns .. ':' .. cell
    else
     fullTitle = cell
    end

    local titleObj = mw.title.new(fullTitle)

    local label
    if labelMode == 'full' then
     label = titleObj and titleObj.text or cell
    else
     label = (titleObj and titleObj.subpageText ~= '') and titleObj.subpageText or (titleObj and titleObj.text or cell)
    end

    cell = '[[' .. fullTitle .. '|' .. label .. ']]'
   end

   cells[#cells + 1] = cell
  end

  out[#out + 1] = '|-'
  out[#out + 1] = '| ' .. table.concat(cells, ' || ')
 end

 out[#out + 1] = '|}'
 return table.concat(out, '\n')
end

function p.query(frame)

 local columns = nil
 if frame.args.columns then
  columns = mw.text.split(frame.args.columns, ',%s*')
 end

 local linkColumns = nil
 if frame.args.linkColumns then
  linkColumns = mw.text.split(frame.args.linkColumns, ',%s*')
 end

 local linkNamespace = {}
 for k, v in pairs(frame.args) do
  if type(k) == 'string' then
   local col = k:match('^linkNamespace(.+)$')
   if col then
    linkNamespace[mw.text.trim(col)] = mw.text.trim(v)
   end
  end
 end

 local labelMode = frame.args.linkLabel or 'subpage'

 return renderRowsAsTable(
  nw.query(frame.args[1]),
  columns,
  linkColumns,
  linkNamespace,
  labelMode
 )
end

local function statementValue(stmt)
 if not stmt or not stmt.values or stmt.values[1] == nil then return nil end
 local v = stmt.values[1]
 if type(v) == 'table' then return v.label or v.target end
 return v
end

function p.childTable(frame)
 local columns = mw.text.split(frame.args.columns or '', ',%s*')
 local schemaFilter = frame.args.schema
 local sortBy = frame.args.sortBy
 local sortDir = frame.args.sortDir or 'desc'

 local numberSet = {}
 if frame.args.numberColumns then
  for _, col in ipairs(mw.text.split(frame.args.numberColumns, ',%s*')) do
   numberSet[col] = true
  end
 end

 local children = nw.getChildSubjects()
 if not children then return '' end

 local lang = mw.getContentLanguage()
 local rows = {}

 for _, child in ipairs(children) do
  if not schemaFilter or child.schema == schemaFilter then
   local row = {}
   for _, col in ipairs(columns) do
    local v = statementValue(child.statements[col])
    if v == nil then
     row[col] = ''
    elseif numberSet[col] and tonumber(v) then
     row[col] = lang:formatNum(tonumber(v))
    else
     row[col] = tostring(v)
    end
   end
   if sortBy then
    row.__sortValue = statementValue(child.statements[sortBy])
   end
   rows[#rows + 1] = row
  end
 end

 if sortBy then
  table.sort(rows, function(a,b)
   local av,bv = a.__sortValue, b.__sortValue
   local an,bn = tonumber(av), tonumber(bv)
   if an and bn then
    if sortDir == 'asc' then return an < bn end
    return an > bn
   end
   if sortDir == 'asc' then return tostring(av) < tostring(bv) end
   return tostring(av) > tostring(bv)
  end)
 end

 return renderRowsAsTable(rows, columns, nil)
end

function p.productsFoundedSince(frame)
 local year = tonumber(frame.args[1]) or 2000
 return renderRowsAsTable(nw.query(
  'MATCH (n:Product) WHERE n.`Available since` >= $year RETURN n.name AS name, n.`Available since` AS year ORDER BY year',
  { year = year }
 ))
end

local function propertyDetails(prop)
 local details = {}

 if prop.type == 'select' then
  local labels = {}
  for _, option in ipairs(prop.options) do
   labels[#labels + 1] = option.label
  end
  details[#details + 1] = 'options: ' .. table.concat(labels, ', ')
 elseif prop.type == 'number' then
  if prop.minimum ~= nil then details[#details + 1] = 'min: ' .. tostring(prop.minimum) end
  if prop.maximum ~= nil then details[#details + 1] = 'max: ' .. tostring(prop.maximum) end
  if prop.precision ~= nil then details[#details + 1] = 'precision: ' .. tostring(prop.precision) end
 elseif prop.type == 'relation' then
  details[#details + 1] = 'targetSchema: ' .. prop.targetSchema
  details[#details + 1] = 'relation: ' .. prop.relation
 elseif prop.type == 'text' or prop.type == 'url' then
  if prop.multiple then details[#details + 1] = 'multiple: true' end
  if prop.uniqueItems then details[#details + 1] = 'uniqueItems: true' end
 end

 return table.concat(details, ', ')
end

function p.schema(frame)
 local schema = nw.getSchema(frame.args[1])
 if not schema then return 'Schema not found' end

 local rows = {}
 for _, prop in ipairs(schema.properties) do
  rows[#rows + 1] = {
   Name = prop.name,
   Type = prop.type,
   Required = prop.required and 'Yes' or 'No',
   Details = propertyDetails(prop)
  }
 end

 return renderRowsAsTable(rows, { 'Name', 'Type', 'Required', 'Details' })
end

return p