Для документации этого модуля может быть создана страница Модуль:WDSource/doc

require('strict')

local p = {}
local wikidata = require('Модуль:WDCommon')
local wdLang = require('Модуль:WDLang')
local backend = require('Модуль:WDBackend')

local cache = require('Модуль:WDSource/Cache')

local langCache = cache.LangCache
local idCache = cache.IdCache

local P_SPECIFIED_AS = 'P1932'
local P_NAMED_AS = 'P1810'
local P_LANG_CODE = 'P218'
local P_WORK_LANG = 'P407'
local P_SUBCLASS_OF = 'P279'
local P_INSTANCE_OF = 'P31'
local P_TITLE = 'P1476'
local P_SUBTITLE = 'P1680'
local P_EDITION_AS_STR = 'P9767'
local P_GENRE = 'P136'
local P_WORK_FORM = 'P7937'
local P_INSCRIPTION = 'P1684'
local P_INSCRIPTION_MENTIONS = 'P6568'
local P_TRANSLATION_OF = 'P9745'
local P_PARTS_COUNT = 'P2635'
local P_AUTHORS = 'P50'
local P_AUTHORS_AS_STR = 'P2093'
local P_ILLUSTRATORS = 'P110'
local P_EDITOR_IN_CHIEF = 'P5769'
local P_EDITORS = 'P98'
local P_TRANSLATORS = 'P655'
local P_CONTAINS = 'P4330'
local P_DATE = 'P577'
local P_START_DATE = 'P580'
local P_END_DATE = 'P582'
local P_URL = 'P953'
local P_URL_STATUS = 'P6954'
local P_ARCHIVE_URL = 'P1065'
local P_ARCHIVE_DATE = 'P2960'
local P_URL_MASK = 'P1630'
local P_SEARCH_URL_MASK = 'P4354'
local P_ISSN = 'P236'
local P_ISBN_13 = 'P212'
local P_ISBN_10 = 'P957'
local P_PUBLISHED_IN = 'P1433'
local P_VOLUME = 'P478'
local P_ISSUE = 'P433'
local P_PAGES = 'P304'
local P_PAGES_COUNT = 'P1104'
local P_PUBLISHER = 'P123'
local P_LOCATION = 'P291'
local P_ARTICLE_ID = 'P2322'
local P_DOI = 'P356'
local P_S2SIC = 'P8299'
local P_OCLC = 'P243'
local P_EDITION_OF = 'P629'
local P_PART_OF = 'P361'
local P_OF_SERIES = 'P179'
local P_GOOGLE_BOOKS_ID = 'P675'
local P_PMID = 'P698'
local P_PMC_ID = 'P932'
local P_PRODUCES = 'P1056'
local P_ROLE = 'P2868'
local P_DEDICATED_TO = 'P825'
local P_FAMILY_NAME = 'P734'
local P_GIVEN_NAME = 'P735'
local P_ANCESTOR_NAME = 'P5056'
local P_WIKIDATA_PROP = 'P1687'

local contentTypeEntities = { 'Q30070675', 'Q108676767', 'Q478798', 'Q60533375', 'Q11424', 'Q187947', 'Q2376293' }

local contributorComponentsMap = {
	{
		name = 'familyName',
		property = P_FAMILY_NAME,
		max = 1,
		isLocal = true,
	},
	{
		name = 'givenName',
		property = P_GIVEN_NAME,
		isLocal = true,
	},
	{
		name = 'ancestorName',
		property = P_ANCESTOR_NAME,
		max = 1,
		isLocal = true,
	},
}

local exactQualifier = {
	property = P_SPECIFIED_AS,
	overwriteValue = true,
	exact = true,
	max = 1,
}

local publishedInOriginLang = {
	name = 'publishedInOriginLang',
	property = P_WORK_LANG,
	get = {
		{
			name = 'publishedInOriginLangCode',
			property = P_LANG_CODE,
			cache = langCache,
			max = 1,
			elseGet = {
				-- for ethnolects determine parent language
				{
					name = 'publishedInOriginLang',
					property = P_SUBCLASS_OF,
					overwrite = true,
					max = 1,
					get = {
						{
							name = 'publishedInOriginLangCode',
							property = P_LANG_CODE,
							cache = langCache,
							overwrite = true,
							max = 1,
						},
					},
				},
			},
		},
	},
}

local publishedInMap = {
	{
		name = 'publishedIn',
		getValue = wikidata.name,
	},
	{
		name = 'publishedInSubtitle',
		property = P_SUBTITLE,
		filter = wikidata.base.tryFilterStatementsByLang,
	},
	{
		name = 'edition',
		property = P_EDITION_AS_STR,
		max = 1,
	},
	{
		name = 'publishedInWorkType',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q1404878', 'Q83790', 'Q2250844', 'Q13136', 'Q23622', 'Q5292', 'Q615699', 'Q5633421' },
	},
	{
		name = 'publishedInIsHosting',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q559856', 'Q15590336', 'Q3220391' },
	},
	{
		name = 'publishedInIsHosting',
		property = P_PRODUCES,
		allowedEntities = { 'Q559856' },
	},
	{
		name = 'publishedInEditionType',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q3331189', 'Q1238720', 'Q571' },
	},
	{
		name = 'publishedInWorkType',
		property = P_WORK_FORM,
	},
	{
		name = 'publishedInIsPeriodic',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q5633421', 'Q847906', 'Q41298', 'Q11032', 'Q1110794', 'Q737498' },
	},
	{
		name = 'publishedInAuthors',
		properties = { P_AUTHORS, P_AUTHORS_AS_STR },
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'publishedInIllustrators',
		property = P_ILLUSTRATORS,
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'isLikeBook',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q571', 'Q128093', 'Q5292' },
	},
	{
		property = P_PRODUCES,
		get = {
			{
				name = 'contentType',
				property = P_CONTAINS,
				allowedEntities = contentTypeEntities,
			},
		},
	},
	{
		name = 'contentType',
		property = P_CONTAINS,
		allowedEntities = contentTypeEntities,
	},
	{
		name = 'publishedInOrigin',
		property = P_TRANSLATION_OF,
		getValue = wikidata.name,
		get = {
			publishedInOriginLang
		},
	},
	{
		name = 'publishedInOrigin',
		property = P_EDITION_OF,
		getValue = wikidata.name,
		get = {
			publishedInOriginLang
		},
	},
	{
		name = 'idType',
		property = P_WIKIDATA_PROP,
		max = 1,
		get = {
			{
				name = 'urlMask',
				property = P_URL_MASK,
				max = 1,
			},
		},
	},
	{
		name = 'urlMask',
		property = P_SEARCH_URL_MASK,
		max = 1,
	},
	{
		name = 'publishedInUrl',
		property = P_URL,
		max = 1,
		qualifiers = {
			{
				name = 'publishedInArchiveUrl',
				property = P_ARCHIVE_URL,
				max = 1,
			},
			{
				name = 'publishedInArchiveDate',
				property = P_ARCHIVE_DATE,
				max = 1,
			},
			{
				name = 'publishedInUrlStatus',
				property = P_URL_STATUS,
				max = 1,
			},
		},
	},
	{
		name = 'publishedInTranslators',
		property = P_TRANSLATORS,
		isArray = true,
		get = contributorComponentsMap,
	},
	{
		name = 'publishedInEditorInChief',
		property = P_EDITOR_IN_CHIEF,
		isArray = true,
		qualifiers = { exactQualifier },
		get = contributorComponentsMap,
	},
	{
		name = 'publishedInEditors',
		property = P_EDITORS,
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'isbn',
		property = P_ISBN_13,
		match = true,
		qualifiers = {
			{
				name = 'date',
				property = P_DATE,
				max = 1,
			},
			{
				name = 'location',
				property = P_LOCATION,
			},
			{
				name = 'publisher',
				property = P_PUBLISHER,
				max = 1,
			},
			{
				name = 'pagesCount',
				property = P_PAGES_COUNT,
				defaultUnit = 'Q1069725',
				max = 1,
			},
		},
		elseGet = {
			{
				name = 'isbn',
				property = P_ISBN_10,
				qualifiers = {
					{
						name = 'date',
						property = P_DATE,
						max = 1,
					},
					{
						name = 'location',
						property = P_LOCATION,
					},
					{
						name = 'publisher',
						property = P_PUBLISHER,
						max = 1,
					},
					{
						name = 'pagesCount',
						property = P_PAGES_COUNT,
						defaultUnit = 'Q1069725',
						max = 1,
					},
				},
			},
		},
	},
	{
		name = 'oclc',
		property = P_OCLC,
	},
	{
		name = 'date',
		property = P_DATE,
	},
	{
		name = 'location',
		property = P_LOCATION,
	},
	{
		name = 'publisher',
		property = P_PUBLISHER,
		qualifiers = {
			{
				property = P_SPECIFIED_AS,
				overwriteValue = true,
				exact = true,
				max = 1,
			},
		},
	},
	{
		name = 'pagesCount',
		property = P_PAGES_COUNT,
		defaultUnit = 'Q1069725',
		max = 1,
	},
	{
		name = 'issn',
		property = P_ISSN,
	},
	{
		name = 'publishedInPartsCount',
		property = P_PARTS_COUNT,
		allowedUnits = { 'Q1238720' },
		max = 1,
	},
}

local workMap = {
	{
		name = 'detectedInfo',
		property = P_INSTANCE_OF,
		isArray = true,
		allowedEntities = { 'Q1404878', 'Q83790', 'Q2250844', 'Q13136', 'Q23622', 'Q5292', 'Q615699', 'Q5633421' },
	},
	{
		name = 'detectedInfo',
		property = P_GENRE,
		isArray = true,
		allowedEntities = { 'Q1404878', 'Q83790', 'Q2250844', 'Q13136', 'Q23622', 'Q5292', 'Q615699', 'Q5633421' },
	},
}

local workVersionMap = {
	{
		name = 'isArticle',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q13442814', 'Q191067' },
	},
	{
		name = 'isIssue',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q28869365', 'Q60534442' },
	},
	{
		name = 'isVolume',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q1238720' },
	},
	{
		name = 'contentType',
		property = P_INSTANCE_OF,
		allowedEntities = contentTypeEntities,
	},
	{
		name = 'workType',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q1404878', 'Q83790', 'Q2250844', 'Q13136', 'Q23622', 'Q5292', 'Q615699', 'Q5633421' },
	},
	{
		name = 'workType',
		property = P_WORK_FORM,
		max = 1,
	},
	{
		name = 'isbn',
		property = P_ISBN_13,
		match = true,
		qualifiers = {
			{
				name = 'date',
				property = P_DATE,
				max = 1,
			},
			{
				name = 'location',
				property = P_LOCATION,
			},
			{
				name = 'publisher',
				property = P_PUBLISHER,
				qualifiers = {
					{
						property = P_SPECIFIED_AS,
						overwriteValue = true,
						exact = true,
						max = 1,
					},
				},
			},
			{
				name = 'pagesCount',
				property = P_PAGES_COUNT,
				defaultUnit = 'Q1069725',
				max = 1,
			},
		},
		elseGet = {
			{
				name = 'isbn',
				property = P_ISBN_10,
				qualifiers = {
					{
						name = 'date',
						property = P_DATE,
						max = 1,
					},
					{
						name = 'location',
						property = P_LOCATION,
					},
					{
						name = 'publisher',
						property = P_PUBLISHER,
						max = 1,
					},
					{
						name = 'pagesCount',
						property = P_PAGES_COUNT,
						defaultUnit = 'Q1069725',
						max = 1,
					},
				},
			},
		},
	},
	{
		name = 'authors',
		properties = { P_AUTHORS, P_AUTHORS_AS_STR },
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'illustrators',
		property = P_ILLUSTRATORS,
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'title',
		getValue = wikidata.name,
	},
	{
		name = 'subtitle',
		property = P_SUBTITLE,
		filter = wikidata.base.tryFilterStatementsByLang,
	},
	{
		name = 'edition',
		property = P_EDITION_AS_STR,
		max = 1,
	},
	{
		name = 'info',
		property = P_INSCRIPTION,
		has = {
			{ property=P_ROLE, value='Q116158574' },
		},
		isArray = true,
		qualifiers = {
			{
				property = P_INSCRIPTION_MENTIONS,
				overwriteEntity = true,
			},
		},
	},
	{
		name = 'editorInChief',
		property = P_EDITOR_IN_CHIEF,
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'editors',
		property = P_EDITORS,
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'translators',
		property = P_TRANSLATORS,
		isArray = true,
		get = contributorComponentsMap,
	},
	{
		name = 'volume',
		property = P_VOLUME,
		qualifiers = {
			{
				name = 'partTitle',
				property = P_TITLE,
				max = 1,
			},
			{
				name = 'volumeTitle',
				property = P_TITLE,
				max = 1,
			},
		},
	},
	{
		name = 'issue',
		property = P_ISSUE,
	},
	{
		name = 'date',
		property = P_DATE,
		qualifiers = {
			{
				name = 'startDate',
				property = P_START_DATE,
				max = 1,
			},
			{
				name = 'endDate',
				property = P_END_DATE,
				max = 1,
			},
		},
	},
	{
		name = 'pages',
		property = P_PAGES,
	},
	{
		name = 'pagesCount',
		property = P_PAGES_COUNT,
		defaultUnit = 'Q1069725',
	},
	{
		name = 'articleId',
		property = P_ARTICLE_ID,
	},
	{
		name = 'url',
		property = P_URL,
		max = 1,
		qualifiers = {
			{
				name = 'archiveUrl',
				property = P_ARCHIVE_URL,
				max = 1,
			},
			{
				name = 'archiveDate',
				property = P_ARCHIVE_DATE,
				max = 1,
			},
			{
				name = 'urlStatus',
				property = P_URL_STATUS,
				max = 1,
			},
		},
	},
	{
		name = 'location',
		property = P_LOCATION,
	},
	{
		name = 'publisher',
		property = P_PUBLISHER,
		qualifiers = {
			{
				property = P_SPECIFIED_AS,
				overwriteValue = true,
				exact = true,
				max = 1,
			},
		}
	},
	{
		name = 'origin',
		property = P_TRANSLATION_OF,
		getValue = wikidata.name,
		get = {
			{
				name = 'originLang',
				property = P_WORK_LANG,
				get = {
					{
						name = 'langCode',
						property = P_LANG_CODE,
						cache = langCache,
						max = 1,
					}
				},
			},
			{
				name = 'originSubtitle',
				property = P_SUBTITLE,
				max = 1,
			},
			{
				name = 'originVolume',
				property = P_VOLUME,
				max = 1,
				qualifiers = {
					{
						name = 'originVolumeTitle',
						property = P_TITLE,
						max = 1,
					},
				},
			},
		},
	},
	{
		name = 'origin',
		property = P_EDITION_OF,
		getValue = wikidata.name,
		get = {
			{
				name = 'originLang',
				property = P_WORK_LANG,
				get = {
					{
						name = 'langCode',
						property = P_LANG_CODE,
						cache = langCache,
						max = 1,
					}
				},
			},
		},
	},
	{
		name = 'partsCount',
		property = P_PARTS_COUNT,
		max = 1,
	},
	{
		name = 'dedicatedTo',
		property = P_DEDICATED_TO,
	},
	{
		name = 'doi',
		property = P_DOI,
	},
	{
		name = 'oclc',
		property = P_OCLC,
	},
	{
		name = 'pmid',
		property = P_PMID,
	},
	{
		name = 'pmc',
		property = P_PMC_ID,
		get = {
			{
				name = 'urlMask',
				entity = P_PMC_ID,
				property = P_URL_MASK,
				cache = idCache,
				max = 1,
				isLocal = true,
			},
		},
	},
	{
		name = 's2sic',
		property = P_S2SIC,
	},
	{
		name = 'publishedIn',
		property = P_PUBLISHED_IN,
		getValue = wikidata.name,
		max = 1,
		qualifiers = {
			-- probably, wrong way, different publications must have different items
			{
				name = 'volume',
				property = P_VOLUME,
			},
			{
				name = 'issue',
				property = P_ISSUE,
			},
			{
				name = 'date',
				property = P_DATE,
			},
			{
				name = 'startDate',
				property = P_START_DATE,
				max = 1,
			},
			{
				name = 'endDate',
				property = P_END_DATE,
				max = 1,
			},
			{
				name = 'pages',
				property = P_PAGES,
			},
			{
				name = 'articleId',
				property = P_ARTICLE_ID,
			},
		},
	},
	{
		name = 'work',
		property = P_EDITION_OF,
		max = 1,
	},
	{
		name = 'publishedIn',
		property = P_PART_OF,
		getValue = wikidata.name,
		max = 1,
		get = {
			{
				name = 'partsCount',
				property = P_PARTS_COUNT,
				allowedUnits = { 'Q1238720' },
				max = 1,
			},
		},
	},
	{
		name = 'series',
		property = P_OF_SERIES,
		max = 1,
		qualifiers = {
			{
				name = 'seriesIssue',
				property = P_ISSUE,
				max = 1,
			},
		},
	},
}

local topicMap = {
	{
		name = 'id',
		propertyPath = { 'idType', 'entity' },
		max = 1,
		qualifiers = {
			{
				name = 'title',
				property = P_NAMED_AS,
				max = 1,
			},
			{
				name = 'date',
				property = P_DATE,
				max = 1,
			},
			{
				name = 'archiveUrl',
				property = P_ARCHIVE_URL,
				max = 1,
			},
			{
				name = 'archiveDate',
				property = P_ARCHIVE_DATE,
				max = 1,
			},
			{
				name = 'authors',
				property = P_AUTHORS_AS_STR,
				isArray = true,
			},
		},
	},
	{
		-- if P1810 id qualifier is not specified
		name = 'title',
		getValue = wikidata.name,
	},
}

local alternativeUrl = {
	{
		name = 'id',
		property = P_GOOGLE_BOOKS_ID,
		max = 1,
		get = {
			{
				name = 'urlMask',
				entity = P_GOOGLE_BOOKS_ID,
				property = P_URL_MASK,
				cache = idCache,
				max = 1,
				overwrite = true,
			},
		},
	},
}

local function fetchUrl(f, source)
	if source.url then
		if source.urlStatus and source.urlStatus.entity == 'Q1193907' then
			source.url = nil
		else
			return
		end
	end

	local idTable = f:safeField(source, 'id')
	if idTable.value and source.urlMask then
		source.url = { value = source.urlMask.value:gsub('%$1', idTable.value) }
		return
	end

	local pmcTable = f:safeField(source, 'pmc')
	if pmcTable.value and pmcTable.components and pmcTable.components.urlMask then
		source.url = {
			value = pmcTable.components.urlMask.value:gsub('%$1', pmcTable.value)
		}
		return
	end
end

local function fetchPublishedInUrl(f, source)
	if source.publishedInUrl then
		if source.publishedInUrlStatus and source.publishedInUrlStatus.entity == 'Q1193907' then
			source.publishedInUrl = nil
		end
	end
end

local function getLangCode(source)
	local langCode = source.langCode
	if type(langCode) == 'table' then
		langCode = langCode.value
	end
	return langCode
end

local function fetchLang(f, source)
	local publishedInTable = source.publishedIn

	local langMapItem = {
		name = 'lang',
		property = P_WORK_LANG,
		get = {
			{
				name = 'langCode',
				property = P_LANG_CODE,
				cache = langCache,
				max = 1,
			},
		},
	}
	if not source.langCode then
		f:fetch(source, {
			{
				name = 'workVersion',
				get = {
					langMapItem,
					{
						name = 'publishedIn',
						property = P_PUBLISHED_IN,
						getValue = wikidata.name,
						max = 1,
						get = {
							langMapItem,
						},
					},
				},
			},
		})
	end

	f:fetch(source, {
		{
			name = 'publishedIn',
			get = {
				{
					name = 'publishedInLang',
					property = P_WORK_LANG,
					max = 1,
					get = {
						{
							name = 'publishedInLangCode',
							property = P_LANG_CODE,
							cache = langCache,
							max = 1,
							elseGet = {
								-- for ethnolects determine parent language
								{
									name = 'publishedInLang',
									property = P_SUBCLASS_OF,
									overwrite = true,
									max = 1,
									get = {
										{
											name = 'publishedInLangCode',
											property = P_LANG_CODE,
											cache = langCache,
											overwrite = true,
											max = 1,
										},
									},
								},
							},
						},
					},
				},
			},
		},
	})

	if not source.langCode then
		if source.publishedInLang then
			source.lang = source.publishedInLang
		end
		if source.publishedInLangCode then
			source.langCode = source.publishedInLangCode
		end
	end
	
	if not source.langCode then
		f:ensureLang()
		source.langCode = { value = f.lang }
	else
		f.lang = getLangCode(source)
	end
	if not source.lang then
		source.lang = {
			entity = wdLang.langEntity(f.lang),
		}
	end
	
	-- publishedIn field need to be empty to get its qualifiers later
	if publishedInTable == nil then
		source.publishedIn = nil
	end
end

-- Remove all duplicates from inscriptions of a book
local function removeInfoDuplicates(source)
	if not source.info then
		return
	end

	for _, infoItem in ipairs(source.info) do
		for i, detectedItem in ipairs(source.detectedInfo) do
			if detectedItem.entity == infoItem.entity then
				table.remove(source.detectedInfo, i)
			end
		end
	end
end

function p.fetch(source)
	local f = backend.new(getLangCode(source))
	fetchLang(f, source)
	f:assertLang()

	f:fetch(source, {
		{
			name = 'workVersion',
			get = workVersionMap,
		},
		{
			name = 'publishedIn',
			get = publishedInMap,
		},
		{
			name = 'topic',
			get = topicMap,
		},
		{
			name = 'work',
			get = workMap,
		},
		{
			name = 'workVersion',
			get = alternativeUrl,
		},
	})

	local titleTable = f:safeField(source, 'title')
	if titleTable.value then
		local workTable = f:safeField(source, 'workVersion')
		titleTable.entity = workTable.entity
	end
	
	removeInfoDuplicates(source)

	fetchUrl(f, source)
	fetchPublishedInUrl(f, source)
	return source
end

return p