Module:Signpost
This module makes indexes of articles for the Signpost. It has two main functions:
- Making formatted lists of articles. See Wikipedia:Wikipedia Signpost/Templates/Article list maker for documentation.
- Counting the number articles that fit certain criteria. See Wikipedia:Wikipedia Signpost/Templates/Article count for documentation.
- Note that this is part of a triumvirate of softwares: the indices are read by the module, but they're also written by SignpostTagger AND Wegweiser. Also SPS.JS is involved in some stuff.
Article data
The article data is stored in index modules organised by year, like Module:Signpost/index/2005, Module:Signpost/index/2006, etc. You can see the full list of index modules here. The data for all Signpost articles for a given year goes in that year's index module.
It is possible to edit the index modules by hand, but the recommended way is to use the SignpostTagger gadget. With SignpostTagger you can edit article data directly from Signpost article pages, and using it eliminates the possibility of making formatting errors or putting the data in the wrong place.
Data points
The index modules each contain a list of articles for that year. Each article has the following data points:
date
: The date the article was published, in YYYY-MM-DD formatsubpage
: The subpage of the article. For example, for the special report "Adminship from the German perspective" located at Wikipedia:Wikipedia Signpost/2012-10-22/Special report, the subpage is "Special report".title
: The article's title.authors
: Array listing authors of the article, as read by Wegweiser and SignpostTagger (based on displayed text from the author field, not links).tags
: A list of tags for the article. For example, the special report mentioned above has the tags "germanwikipedia", "reformingrfa", "requestsforadminship", and "specialreport" (optional).views
: Set of key-value pairs for views over seven intervals:d007
(7 days),d015
(15 days),d030
,d060
,d090
,d120
,d180
.subhead
: Article subheading (not normally displayed in the article, but can be parsed out of the "RSS description" template).piccy
: Set of key-value pairs for associating an image with an article (optional).
filename
: Image credit to display as overlay (i.e. author of image).license
: Short string for image license in overlay (i.e. "CC 4.0 BY-SA").scaling
: Scaling, width-based: default is 300, which is the width of the snippet template.xoffset
: X-offset (i.e. how many pixels to crop from the left if scaling gives an image wider than 300px).yoffset
: Y-offset (i.e. how many pixels to crop from the top if scaling gives an image taller than 300px).
Tags and aliases
Tags are used to classify articles. For example, you can find all of the humourous articles by using the article list maker template to list all articles with the tag "humour". It is also possible to specify aliases for tags. For example, "humor" is an alias for the "humour" tag, so when you are tagging an article with SignpostTagger you can use either one of them. Tag aliases are stored at Module:Signpost/aliases.
Subpages
Test cases
Wikipedia:Wikipedia Signpost/Templates/Article list maker, sort by tag (arbitrationreport), limit 5, include subhead Script error: The function "main" does not exist.
local INDEX_MODULE = 'Module:Signpost/index'
local lang = mw.language.getContentLanguage()
--------------------------------------------------------------------------------
-- Article class
--------------------------------------------------------------------------------
local Article = {}
Article.__index = Article
function Article.new(data)
local self = setmetatable({}, Article)
self.data = data
return self
end
function Article:getSortKey()
return self.data.sortKey
end
function Article:getPage()
return self.data.page
end
function Article:getDate()
return self.data.date
end
function Article:getTitle()
return self.data.title
end
function Article:getSubpage()
return self.data.subpage
end
function Article:hasAllTags(t)
local tags = self.data.tags
for i, testTag in ipairs(t) do
local hasTag = false
for j, tag in ipairs(tags) do
if tag == testTag then
hasTag = true
end
end
if not hasTag then
return false
end
end
return true
end
--------------------------------------------------------------------------------
-- List class
--------------------------------------------------------------------------------
local List = {}
List.__index = List
List.rowParams = {
PAGE = 'getPage',
DATE = 'getDate',
TITLE = 'getTitle',
SUBPAGE = 'getSubpage',
}
function List.new(args, index)
local self = setmetatable({}, List)
self.index = index or mw.loadData(INDEX_MODULE)
self.rowtemplate = args.rowtemplate
self.rowformat = args.rowformat
-- Get article objects, filtered by page, date and tag, and sort them.
if args.page then
self.articles = { self:getPageArticle(args.page) }
elseif args.date then
self.articles = self:getDateArticles(args.date)
else
self.articles = self:getTagArticles(args.tags, args.tagmatch)
if not self.articles then
self.articles = self:getAllArticles()
end
self:filterArticlesByDate(args.startdate, args.enddate)
end
self:sortArticles(args.sortdir, args.sortfield)
return self
end
-- Static methods
function List.parseTagString(s)
local tags = mw.text.split(s, '%s*,%s')
local ret = {}
for i, tag in ipairs(tags) do
if tag ~= '' then
table.insert(ret, mw.ustring.lower(tag))
end
end
return ret
end
function List.normalizeDate(date)
if not date then
return nil
end
return lang:formatDate('Y-m-d', date)
end
-- Normal methods
function List:getPageArticle(page)
local data = self.index.pages[page]
if not data then
error(string.format(
'[[%s]] is not an indexed Signpost article',
page
), 2)
end
return Article.new(data)
end
function List:getDateArticles(date)
date = self.normalizeDate(date)
local dates = self.index.dates[date]
if not dates then
error(string.format(
'no Signpost articles are indexed for the date %s',
date
), 2)
end
local ret = {}
for i, data in ipairs(dates) do
ret[i] = Article.new(data)
end
return ret
end
function List:getTagArticles(s, tagMatch)
if not s then
return nil
end
local tagIndex = self.index.tags
local ret, pages = {}, {}
local tags = self.parseTagString(s)
for i, tag in ipairs(tags) do
local data = tagIndex[tag]
if data then
local obj = Article.new(data)
pages[obj:getPage()] = obj
end
end
for page, obj in pairs(pages) do
if not tagMatch
or tagMatch == 'any'
or tagMatch == 'all' and obj:hasAllTags(tags)
then
table.insert(ret, obj)
end
end
if #ret < 1 then
error(string.format('no valid tags found in the string "%s"', s), 2)
end
return ret
end
function List:getAllArticles()
local ret = {}
for i, data in ipairs(self.index.list) do
ret[i] = Article.new(data)
end
return ret
end
function List:filterArticlesByDate(startDate, endDate)
startDate = self.normalizeDate(startDate) or '2005-01-01'
endDate = self.normalizeDate(endDate) or lang:formatDate('Y-m-d')
local ret = {}
for i, article in ipairs(self.articles) do
local date = article:getDate()
if startDate <= date and date <= endDate then
table.insert(ret, article)
end
end
self.articles = ret
end
function List:sortArticles(direction, field)
local accessor
if not field or field == 'date' then
accessor = function (article) return article:getSortKey() end
elseif field == 'page' then
accessor = function (article) return article:getPage() end
elseif field == 'title' then
accessor = function (article) return article:getTitle() end
else
error(string.format("'%s' is not a valid sort field", field), 2)
end
local sortFunc
if not direction or direction == 'ascending' then
sortFunc = function (a, b)
return accessor(a) < accessor(b)
end
elseif direction == 'descending' then
sortFunc = function (a, b)
return accessor(a) > accessor(b)
end
else
error(string.format("'%s' is not a valid sort direction", direction), 2)
end
table.sort(self.articles, sortFunc)
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p = {}
function p._tag(tag)
local data = mw.loadData(INDEX_MODULE)
if not tag then
return nil
end
local articles = data.tags[tag]
if not articles then
return nil
end
local ret = {}
ret[#ret + 1] = '<ul>'
for i, t in ipairs(articles) do
ret[#ret + 1] = string.format(
'<li>%s, %s: [[%s|%s]]</li>',
t.date, t.subpage, t.page, t.title
)
end
ret[#ret + 1] = '</ul>'
return table.concat(ret, '\n')
end
function p.tag(frame)
local tag = frame.args[1]
return p._tag(tag)
end
return p