Jump to content

Module:Work license

From Wikisource
--[=[
A module that can be used to get licensing information about an entity

WORK IN PROGRESS
]=]

local p = {} --p stands for package

-- library util provided by MW
local util = require 'libraryUtil'

local wikibaseUtils = require( 'Module:Wikibase utils' )

-- common constants
local DATA = mw.loadData( 'Module:Work data/properties' )

-- local constants useful mainly for this module
local LDATA = {
	props = {
		jurisdiction = 'P1001',
		determinationMethod = 'P459',
		relevantCopyrightDate = 'P9905'
	},
	items = {
		usa = 'Q30',
		publicDomain = 'Q19652',
		
		pma50Countries = 'Q59621182',
		pma70Countries = 'Q73560261',
		
		pma25orLessCountries = 'Q68543134',
		pma60orLessCountries = 'Q105480667',
		pma70orLessCountries = 'Q59542795',
		pma80orLessCountries = 'Q61830521',
		pma100orLessCountries = 'Q60332278',
		
		pma50orMoreCountries = 'Q87048619',
		pma70orMoreCountries = 'Q60845045',
		
		countriesWithPDArt = 'Q108574927',
		countriesWithoutPDArt = 'Q108574944',
		
		pubOver95YearsAgo = 'Q47246828',
		byUsFederalGov = 'Q60671452',
		noUsRenweal = 'Q29941035',
		noUsNotice = 'Q47012574',
		badUsNotice = 'Q61062601',
		
		pma100Determination = 'Q29940705',
	}
}

-- "extra" jurisdications that countries belong to and can't be worked out
-- by maths
local JURISDICTIONS = {
	[ LDATA.items.usa ] = {
		LDATA.items.countriesWithPDArt,
	}
}
local PMAS = {
}

local function getJurisdictionsForCountry( countryQid )
	-- always contains the country itself
	local jurisdictions = {
		countryQid
	}
	-- add "specials"
	for _, j in pairs( JURISDICTIONS[ countryQid ] or {} ) do
		table.insert( jurisdictions, j )
	end
	
	local pma = PMAS[ countryQid ]
	if pma ~= nil then
		-- work out the membership of the PMA items
		-- these are incomplete at WD, so this looks wierd, but it does what it
		-- needs to for us
		
		-- XX or less groupins
		if pma <= 25 then
			table.insert( jurisdictions, LDATA.items.pma25orLessCountries )
		end
		if pma <= 60 then
			table.insert( jurisdictions, LDATA.items.pma60orLessCountries )
		end
		if pma <= 70 then
			table.insert( jurisdictions, LDATA.items.pma70orLessCountries )
		end
		if pma <= 80 then
			table.insert( jurisdictions, LDATA.items.pma80orLessCountries )
		end
		if pma <= 100 then
			table.insert( jurisdictions, LDATA.items.pma100orLessCountries )
		end
		
		-- exact PMA groupings
		if pma == 50 then
			table.insert( jurisdictions, LDATA.items.pma50Countries )
		elseif pma == 70 then
			table.insert( jurisdictions, LDATA.items.pma70Countries )
		end
		
		-- or more groupings
		if pma >= 70 then
			table.insert( jurisdictions, LDATA.items.pma70orMoreCountries )
		end
		if pma >= 50 then
			table.insert( jurisdictions, LDATA.items.pma50orMoreCountries )
		end
	else
		-- error('Do not know the PMA for the requested country: ' .. countryQid )
	end
	return jurisdictions
end

--[=[
Get any qualifiers of the given statement with a QID in the given list
Returns a table of snaks, or an empty table
]=]
local function getStatementQualifiersWithValueQids( statement, pid, qids )
	if type( qids ) ~= 'table' then
		qids = { qids }
	end
	
	local matchingQualifiers = {}

	-- look in every suitable qualifier of this statement
	local qualifiersOfProperty = wikibaseUtils.getQualifiersOfStatementWithPid( statement, pid )
	
	-- for every qualifier, see if it has one of the desired values
	for _, qualifierSnak in pairs( qualifiersOfProperty ) do
		local snakValue = wikibaseUtils.getSnakValueQid( qualifierSnak )
		for _, qid in pairs( qids ) do
			if snakValue == qid then
				table.insert( matchingQualifiers, qualifierSnak )
			end
		end
	end
	return matchingQualifiers
end
	


--[=[
Get the list of the copyright claims that apply to the given country
]=]
local function getApplicableCopyrightStatusClaims( entity, countryQid ) 
	local copyrightStatusClaims = entity:getAllStatements( DATA.props.copyrightStatus )
	-- the list of jurisdictions that this country belongs to
	local allowedJurisdictions = getJurisdictionsForCountry( countryQid )
	
	local applicableClaims = {}
	
	for _, statement in pairs( copyrightStatusClaims ) do
		local applicableClaimsForStatement = getStatementQualifiersWithValueQids(
			statement, LDATA.props.jurisdiction, allowedJurisdictions )

		if #applicableClaimsForStatement > 0 then
			table.insert( applicableClaims, statement )
		end
	end
	
	-- did not find any statement indicating PD
	return applicableClaims
end

--[=[
Determine if a work is public domain in the given country
]=]
local function isEntityPdInCountry( entity, countryQid ) 
	local applicableClaims = getApplicableCopyrightStatusClaims( entity, countryQid )
	
	for _, claim in pairs( applicableClaims ) do
		if wikibaseUtils.getSnakValueQid( claim.mainsnak ) == LDATA.items.publicDomain then
			return true	
		end
	end
	-- no claim indicates PD in the given country
	return false
end

local function getBestRelevantDate( claim )
	local dates = wikibaseUtils.getQualifiersOfStatementWithPid(
		claim, LDATA.props.relevantCopyrightDate )
	local relevantDate
	for _, dateQual in pairs( dates ) do
		relevantDate = wikibaseUtils.getSnakValueYear( dateQual )
	end	
	return relevantDate
end

local function getUsLicenceFromClaim( claim )
	local status = wikibaseUtils.getSnakValueQid( claim.mainsnak )
	-- list of determination qualifers on the claim
	local determinations = wikibaseUtils.getQualifiersOfStatementWithPid(
		claim, LDATA.props.determinationMethod )
	local relevantDate = getBestRelevantDate( claim )
	-- convert suitable determination qualifers to a simple representation
	for _, det in pairs( determinations ) do
		-- the Qid of the determination method
		local detQid = wikibaseUtils.getSnakValueQid( det )

		if status == LDATA.items.publicDomain then
			if detQid == LDATA.items.pubOver95YearsAgo then
				lic = {
					nature = 'pub-date',
					date = relevantDate
				}
			elseif detQid == LDATA.items.byUsFederalGov then
				lic = {
					nature = 'us-gov',
					date = nil -- date doesn't matter
				}
			elseif detQid == LDATA.items.noUsRenweal then
				lic = {
					nature = 'not-renewed',
					date = relevantDate
				}
			elseif detQid == LDATA.items.noUsNotice then
				lic = {
					nature = 'no-notice',
					date = relevantDate
				}
			elseif detQid == LDATA.items.badUsNotices then
				lic = {
					nature = 'no-notice',
					date = relevantDate
				}
			end
		end

		if lic ~= nil then
			return lic
		end
	end
	return nil
end

--[=[
Get the US and non-US license data
]=]
local function getEntityLicenseData( entity )
	local usLicenses
	local otherLicenses

	-- get all the US-applicable copyright statements
	local usClaims = getApplicableCopyrightStatusClaims( entity, LDATA.items.usa )
	
	for _, claim in pairs( usClaims ) do
		local usLic = getUsLicenceFromClaim( claim)
		-- probably just need one(?)
		if usLic ~= nil then
			usLicenses = usLic
			break
		end
	end
	
	return {
		us = usLicenses,
		other = otherLicenses
	}	
end

--[=[
This is the main entry point to get a new workLicense object

This function is public, and is designed to be called by other modules
]=]
function p.newWorkLicense( titleOrQid )
	-- the object we will eventually return
	local obj = {}
	-- the function that checks if a call to a method is using . instead of :
	local checkSelfFunc = util.makeCheckSelfFunction( 'Module:Work license',
		'aWorkLicense', obj, 'work license object' );
	
	local entity = wikibaseUtils.getEntity( titleOrQid )
	
	-- bail out if we don't find a WB entity
	if entity == nil then
		return nil
	end
	
	-- data object of this object
	-- this is private, and access is granted though the returned meta-table
	local data = {
		entity = entity,
	}

	return setmetatable( obj, {
		__eq = entity.id.equals,
		__lt = entity.id.__lt,
		__tostring = function ( t )
			return t.entity
		end,
		-- return specific fields
		__index = function ( t, key )
			
			-- figure out if a work is PD at Wikisource (i.e. in the US specifically)
			-- returns true, false or nil (meaning cannot be determined)
			if key == 'isPublicDomain' then
				if data.isPublicDomain == nil then
					data.isPublicDomain = isEntityPdInCountry( entity, LDATA.items.usa )
				end
				return data.isPublicDomain
			end
			
			if key == 'licenseData' then
				if data.licenseData == nil then
					data.licenseData = getEntityLicenseData( entity )
				end
				return data.licenseData
			end
			
			-- otherwise, just return the member of data
			return data[ key ]
		end,
		-- workLicense objects are always read-only
		__newindex = function ( t, k, v )
			error( "index '" .. k .. "' is read only", 2 )
		end
	} )
end

return p