Module:Work

--[=[ WORK IN PROGRESS

The Work module provides a class object that represents a single work via Wikidata. This can be an edition or a work item.

This is a Lua-only module that is intended as a core module for other modules, rather than being directly called by templates.

Generally, the object is initialised with newWork( id_or_title ) and properties are accessed though members of the returned object (the same general princible as proofreadPage.newIndex, mw.title, etc.) ]=]

local p = {} --p stands for package

-- This is provided by the core Scribunto library local util = require 'libraryUtil'

-- Creator objects, used for authors, etc local Creator = require('Module:Creator')

-- Module constants p.TYPES = { EDITION = 'edition', WORK = 'work', ARTICLE = 'article' --scholarly articles }

-- TODO: ship out to /data when this gets big enough to be a memory burden local PROPS = { isA = 'P31', title = 'P1476', pubDate = 'P577', author = 'P50', translator = 'P655', editor = 'P98', illustrator = 'P110', volume = 'P478', issue = 'P433', pages = 'P304', publishedIn = 'P1433', inception = 'P571' }

local ENTITIES = { human = 'Q5', edition = 'Q3331189', scholarlyArticle = 'Q13442814', creativeWork = 'Q17537576', }

--[=[ Util function: return true if the item is in the given table ]=] local function valueIn( t, v ) for _, tv in pairs( t ) do		if tv == v then return true end end return false end

--[=[ Get the entity for a QID, or a page title ]=] local function getEntity( titleOrQid ) local item if type(titleOrQid) == 'table' then -- this is already Wikibase item data item = titleOrQid elseif string.match( titleOrQid, 'Q%d+', 1 ) then -- it's a QID item = mw.wikibase.getEntity( titleOrQid ) else -- assume it's a page title -- TODO only return the ID and look it up (expensive) only if actually used item = mw.wikibase.getEntity(			mw.wikibase.getEntityIdForTitle( titleOrQid )		) end return item end

local function getPropIds( entity, prop ) local ids = {} if not entity.claims[ prop ] then return nil end for _, v in pairs( entity.claims[ prop ] ) do		if v.mainsnak.snaktype == 'value' and v.mainsnak.datatype == 'wikibase-item' then table.insert( ids, v.mainsnak.datavalue.value.id ) end end return ids end

local function getPropEntities( entity, prop ) local ids = getPropIds( entity, prop ) if not ids then return nil end local ents = {} for _, id in pairs( ids ) do		table.insert( ents, mw.wikibase.getEntity( id ) ) end return ents end

local function getLocalStringProp( entity, prop ) local lang = 'en' if not entity.claims[ prop ] then error("No claim found for property: " .. prop) end return entity.claims[ prop ][ 1 ].mainsnak.datavalue.value.text end

local function getStringProp( entity, prop ) if not entity.claims[ prop ] then error("No claim found for property: " .. prop) end return entity.claims[ prop ][ 1 ].mainsnak.datavalue.value end

local function getYearProp( entity, prop ) if not entity.claims[ prop ] then return nil end local v = entity.claims[ prop ][ 1 ] if v.mainsnak.datavalue.type == 'time' then local timestamp = v.mainsnak.datavalue.value.time timestamp = timestamp :gsub( '^%+', '' ) :gsub( '-.*', '' ) -- decade precision if v.mainsnak.datavalue.value.precision == 8 then timestamp = timestamp .. 's'		end return timestamp end end

local function getLocalSiteLink( entity ) return entity:getSitelink( 'enwikisource' ) end

--[=[ Get the 'type' of the work

Options are: edition, articles (as in journal articles), or works ]=] local function getWorkType( entity ) local isVals = getPropIds( entity, PROPS.isA ) if valueIn( isVals, ENTITIES.edition ) then return p.TYPES.EDITION elseif valueIn( isVals, ENTITIES.scholarlyArticle ) then return p.TYPES.ARTICLE end -- assume it's a work, but maybe we should be more careful? return p.TYPES.WORK end

--[=[ Get the list of Creator objects for this property ]=] local function getCreatorList( item, prop ) local creators = getPropEntities( item, prop ) if not creators or #creators == 0 then return nil end local list = {} for _, v in pairs( creators ) do		local creatorObject = Creator.newCreator( v ) table.insert( list, creatorObject ) end return list end

--[=[ The main entry point ]=] function p.newWork( titleOrQid ) local obj = {}

-- the function that checks if a call to a method is using. instead of : local checkSelfFunc = util.makeCheckSelfFunction( 'Module:Work',		'aWork', obj, 'work object' );

local item = getEntity( titleOrQid ) if not item then mw.error( 'Failed to look up item: ' .. titleOrQid ) end

local data = { item = item }	local getType = function if data.type == nil then data.type = getWorkType( item ) end return data.type end return setmetatable( obj, {		__eq = item.equals,		__lt = item.__lt,		__tostring = function ( t )			return item.prefixedText		end,		__index = function ( t, k )			-- lazily load the relvant properties only when actually needed			if k == 'type' then				return getType			end

if k == 'wsPage' then if data.wsPage == nil then data.wsPage = getLocalSiteLink( item ) end return data.wsPage end

if k == 'title' then if data.title == nil then data.title = getLocalStringProp( item, PROPS.title ) end return data.title end if k == 'pubYear' then if data.pubYear == nil then local theDate -- for works specifically, check inception first if getType == 'work' then theDate = getYearProp( item, PROPS.inception ) end if not theDate then theDate = getYearProp( item, PROPS.pubDate ) end data.pubYear = theDate end return data.pubYear end if k == 'author' then if data.author == nil then data.author = getCreatorList( item, PROPS.author ) end return data.author end if k == 'illustrator' then if data.illustrator == nil then data.illustrator = getCreatorList( item, PROPS.illustrator ) end return data.illustrator end if k == 'editor' then if data.editor == nil then data.editor = getCreatorList( item, PROPS.editor ) end return data.editor end if k == 'translator' then if data.translator == nil then data.translator = getCreatorList( item, PROPS.translator ) end return data.translator end if k == 'parentWork' then if data.parentWork == nil then local id = getPropIds( item, PROPS.publishedIn )[ 1 ] if id then data.parentWork = p.newWork( id ) end end return data.parentWork end return data[k] end, __newindex = function ( t, k, v ) error( "index '" .. k .. "' is read only", 2 ) end } ) end

return p