Модуль:CiteGost/WDSource

Материал из wikixw
Перейти к навигации Перейти к поиску

Данный модуль предназначен для получения данных об источнике информации по элементам Викиданных. Работает на основе модуля Модуль:WDBackend.

Модуль используется модулем Модуль:CiteGost для оформления библиографических записей и по своей сути является его обособленной частью.

Схема источника[править]

Общая схема источника в виде иерархии таблиц:

Тесты[править]

В качестве тестов используются тесты вышележащего модуля Модуль:CiteGost.

Ошибка Lua в package.lua на строке 80: module 'Модуль:I18n' not found.

Разработка[править]

См. также[править]

  • CiteGost — модуль для форматирования информации об источнике, получающий информацию посредством модуля WDSource.

require('strict')

local p = {}

local NS_MODULE = 828 --: https://www.mediawiki.org/wiki/Extension_default_namespaces
local moduleNamespace = mw.site.namespaces[NS_MODULE].name

local wikidata = require(moduleNamespace .. ':WDCommon')
local wdLang = require(moduleNamespace .. ':WDLang')
local backend = require(moduleNamespace .. ':WDBackend')

local cache = mw.loadData(moduleNamespace .. ':CiteGost/WDSource/Cache')

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_OBJECT_HAS_ROLE = 'P3831'
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 Q_FREE_ACCESS = 'Q24707952'

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 = 'langCode',
			property = P_LANG_CODE,
			isLocal = true,
			cache = cache,
			max = 1,
			elseGet = {
				-- for ethnolects determine parent language
				{
					property = P_SUBCLASS_OF,
					overwriteEntity = true,
					max = 1,
					get = {
						{
							name = 'langCode',
							property = P_LANG_CODE,
							isLocal = true,
							cache = cache,
							overwriteEntity = true,
							max = 1,
						},
					},
				},
			},
		},
	},
}

local issueMap = {
	{
		name = 'issue',
		property = P_ISSUE,
		qualifiers = {
			{
				name = 'issueTitle',
				property = P_NAMED_AS,
				max = 1,
			},
		},
	},
	{
		name = 'issueTitle',
		property = 'P1476',
	},
	{
		name = 'volume',
		property = P_VOLUME,
		qualifiers = {
			{
				name = 'volumeTitle',
				property = P_NAMED_AS,
				max = 1,
			},
		},
	},
	{
		name = 'publishedIn',
		property = P_PUBLISHED_IN,
		getData = wikidata.nameData,
		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 = 'editors',
		property = P_EDITORS,
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'pagesCount',
		property = P_PAGES_COUNT,
		defaultUnit = 'Q1069725',
		max = 1,
	},
}

local volumeMap = {
	{
		name = 'volume',
		property = P_VOLUME,
		qualifiers = {
			{
				name = 'volumeTitle',
				property = P_NAMED_AS,
				max = 1,
			},
		},
	},
	{
		name = 'volumeTitle',
		property = 'P1476',
	},
	{
		name = 'publishedIn',
		property = P_PUBLISHED_IN,
		getData = wikidata.nameData,
		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 = 'editors',
		property = P_EDITORS,
		isArray = true,
		qualifiers = { exactQualifier },
		skipGetIf = { exact = true },
		get = contributorComponentsMap,
	},
	{
		name = 'pagesCount',
		property = P_PAGES_COUNT,
		defaultUnit = 'Q1069725',
		max = 1,
	},
}

local publishedInMap = {
	{
		name = 'publishedIn',
		getData = wikidata.nameData,
	},
	{
		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,
		useDefaultLang = true,
		allowedEntities = { 'Q559856', 'Q15590336', 'Q3220391' },
	},
	{
		name = 'publishedInIsHosting',
		property = P_PRODUCES,
		useDefaultLang = true,
		allowedEntities = { 'Q559856' },
	},
	{
		name = 'publishedInEditionType',
		property = P_INSTANCE_OF,
		allowedEntities = { 'Q3331189', 'Q1238720', 'Q571' },
	},
	{
		name = 'publishedInWorkType',
		property = P_WORK_FORM,
	},
	{
		name = 'publishedInIsPeriodic',
		property = P_INSTANCE_OF,
		useDefaultLang = true,
		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,
		useDefaultLang = true,
		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,
		getData = wikidata.nameData,
		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 = 'archiveUrl',
				property = P_ARCHIVE_URL,
				isLocal = true,
				max = 1,
			},
			{
				name = 'archiveDate',
				property = P_ARCHIVE_DATE,
				isLocal = true,
				max = 1,
			},
			{
				name = 'urlStatus',
				property = P_URL_STATUS,
				useDefaultLang = true,
				isLocal = true,
				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,
		useDefaultLang = true,
		allowedEntities = { 'Q1404878', 'Q83790', 'Q2250844', 'Q13136', 'Q23622', 'Q5292', 'Q615699', 'Q5633421' },
	},
	{
		name = 'detectedInfo',
		property = P_GENRE,
		isArray = true,
		useDefaultLang = true,
		allowedEntities = { 'Q1404878', 'Q83790', 'Q2250844', 'Q13136', 'Q23622', 'Q5292', 'Q615699', 'Q5633421' },
	},
}

local workVersionMap = {
	{
		name = 'isArticle',
		property = P_INSTANCE_OF,
		useDefaultLang = true,
		allowedEntities = { 'Q13442814', 'Q191067' },
	},
	{
		name = 'isIssue',
		property = P_INSTANCE_OF,
		useDefaultLang = true,
		allowedEntities = { 'Q28869365', 'Q60534442' },
	},
	{
		name = 'isVolume',
		property = P_INSTANCE_OF,
		useDefaultLang = true,
		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',
		getData = wikidata.nameData,
	},
	{
		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 = 'volumeTitle',
				property = P_NAMED_AS,
				max = 1,
			},
		},
	},
	{
		name = 'issue',
		property = P_ISSUE,
		qualifiers = {
			{
				name = 'issueTitle',
				property = P_NAMED_AS,
				max = 1,
			},
		},
	},
	{
		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,
				isLocal = true,
				max = 1,
			},
			{
				name = 'archiveDate',
				property = P_ARCHIVE_DATE,
				isLocal = true,
				max = 1,
			},
			{
				name = 'urlStatus',
				property = P_URL_STATUS,
				useDefaultLang = true,
				isLocal = true,
				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,
		getData = wikidata.nameData,
		get = {
			{
				name = 'originLang',
				property = P_WORK_LANG,
				get = {
					{
						name = 'langCode',
						property = P_LANG_CODE,
						isLocal = true,
						cache = cache,
						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 = 'partsCount',
		property = P_PARTS_COUNT,
		max = 1,
	},
	{
		name = 'dedicatedTo',
		property = P_DEDICATED_TO,
	},
	{
		name = 'doi',
		property = P_DOI,
		qualifiers = {
			{
				name = 'urlStatus',
				property = P_URL_STATUS,
				useDefaultLang = true,
				isLocal = true,
				max = 1,
			},
		},
	},
	{
		name = 'oclc',
		property = P_OCLC,
	},
	{
		name = 'pmid',
		property = P_PMID,
	},
	{
		name = 'pmc',
		property = P_PMC_ID,
		qualifiers = {
			{
				name = 'isManuscript',
				property = P_OBJECT_HAS_ROLE,
				allowedEntities = { 'Q2376293' },
				isLocal = true,
				max = 1,
			},
		},
		get = {
			{
				name = 'urlMask',
				entity = P_PMC_ID,
				property = P_URL_MASK,
				cache = cache,
				max = 1,
				isLocal = true,
			},
			{
				name = 'urlStatus',
				entity = Q_FREE_ACCESS, -- Free to read
				useDefaultLang = true,
				cache = cache,
				getLabel = true,
				isLocal = true,
			},
		},
	},
	{
		name = 's2sic',
		property = P_S2SIC,
	},
	{
		name = 'publishedIn',
		property = P_PUBLISHED_IN,
		getData = wikidata.nameData,
		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,
		getData = wikidata.nameData,
		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,
		substInto = {
			name = 'url',
			template = {
				name = 'urlMask',
			},
		},
		qualifiers = {
			{
				name = 'title',
				property = P_NAMED_AS,
				max = 1,
			},
			{
				name = 'date',
				property = P_DATE,
				max = 1,
			},
			{
				name = { 'url', 'archiveUrl' },
				property = P_ARCHIVE_URL,
				max = 1,
			},
			{
				name = { 'url', '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',
		getData = wikidata.nameData,
	},
}

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

local substMap = {
	{
		name = 'id',
		substInto = {
			name = 'url',
			template = {
				name = 'urlMask',
			},
		},
	},
}

local function fetchUrl(f, source)
	if source.url and source.url.value then
		if not source.url.retrieved then
			return
		end
		if not source.url.components then
			return
		end
		local urlStatus = source.url.components.urlStatus
		if not urlStatus or urlStatus.entity ~= 'Q1193907' then
			return
		end
	end

	local pmcTable = f:safeField(source, 'pmc')
	if pmcTable.value and pmcTable.components and pmcTable.components.urlMask then
		local value, lang = mw.wikibase.getLabelWithLang('Q232932')
		source.url = {
			value = pmcTable.components.urlMask.value:gsub('%$1', pmcTable.value),
			components = {
				urlStatus = pmcTable.components.urlStatus,
			},
		}
		if pmcTable.components.isManuscript then
			local detectedInfoTable = f:safeField(source, 'detectedInfo')
			table.insert(detectedInfoTable, pmcTable.components.isManuscript)
			source.detectedInfo = detectedInfoTable
		end
		
		return
	end
end

local function getLangCode(source)
	if not source.lang then
		return nil
	end

	if table.getn(source.lang) > 0 then
		for _, lang in ipairs(source.lang) do
			if lang.components and lang.components.langCode then
				return lang.components.langCode.value
			end
		end
		return nil
	else
		if not source.lang or not source.lang.components or not source.lang.components.langCode then
			return nil
		end
		return source.lang.components.langCode.value
	end
end

local function checkAndFixPublishedInQid(f, source)
	f:fetch(source, {
		{
			name = 'publishedIn',
			get = {
				{
					name = 'publishedInIsIssue',
					property = P_INSTANCE_OF,
					allowedEntities = { 'Q28869365', 'Q60534442' },
				},
				{
					name = 'publishedInIsVolume',
					property = P_INSTANCE_OF,
					allowedEntities = { 'Q1238720' },
				},
			},
		},
	})

	if source.publishedInIsIssue then
		source.issueVersion = { entity = source.publishedIn.entity }
	elseif source.publishedInIsVolume then
		source.volumeVersion = { entity = source.publishedIn.entity }
	end

	if source.publishedInIsIssue or source.publishedInIsVolume then
		f:fetch(source, {
			{
				name = 'publishedIn',
				get = {
					{
						name = 'publishedIn',
						property = P_PART_OF,
						overwrite = true,
					},
					{
						name = 'publishedIn',
						property = P_PUBLISHED_IN,
						overwrite = true,
					},
				},
			},
		})
	end
	if source.publishedIn and source.publishedIn.retrieved then
		source.publishedIn.value = nil
	end
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,
				isLocal = true,
				cache = cache,
				max = 1,
			},
		},
	}
	if not source.langCode then
		f:fetch(source, {
			{
				name = 'workVersion',
				get = {
					langMapItem,
					{
						name = 'publishedIn',
						property = P_PUBLISHED_IN,
						getData = wikidata.nameData,
						max = 1,
					},
				},
			},
		})
	end

	if not source.langCode then
		f:fetch(source, {
			{
				name = 'issueVersion',
				get = {
					langMapItem,
					{
						name = 'publishedIn',
						property = P_PUBLISHED_IN,
						getData = wikidata.nameData,
						max = 1,
					},
				},
			},
		})
	end

	if not source.langCode then
		f:fetch(source, {
			{
				name = 'volumeVersion',
				get = {
					langMapItem,
					{
						name = 'publishedIn',
						property = P_PUBLISHED_IN,
						getData = wikidata.nameData,
						max = 1,
					},
				},
			},
		})
	end

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

	if not source.lang then
		if source.publishedInLang then
			source.lang = source.publishedInLang
		end
	end
	
	if source.lang then
		f.lang = getLangCode(source)
	end

	f:ensureLang()

	if not source.lang then
		source.lang = {
			entity = wdLang.langEntity(f.lang),
			isDefault = true,
			components = {
				langCode = {
					value = f.lang,
					lang = 'en',
				},
			},
		}
	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 source.info then
		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

	if source.detectedInfo then
		local title
		if source.title then
			title = source.title.value
		end
		if title then
			for i, detectedItem in ipairs(source.detectedInfo) do
				-- Case sensitive, does not cover all cases
				if title:find(detectedItem.value, nil, true) then
					table.remove(source.detectedInfo, i)
				end
			end
		end
	end
	if source.detectedInfo and next(source.detectedInfo) == nil then
		source.detectedInfo = nil
	end
end

local function fixIssueTitle(source)
	if not source.isIssue or not source.title or not source.publishedIn then
		return
	end
	
	if source.title.value ~= source.publishedIn.value then
		if not source.title.fromLabel then
			source.issueTitle = source.title
		end
		source.title = {
			value = source.publishedIn.value,
			lang = source.publishedIn.lang,
			fromLabel = source.publishedIn.fromLabel,
			retrieved = source.publishedIn.retrieved,
		}
	end
end

local function fixVolumeTitle(source)
	if not source.isVolume or not source.title or not source.publishedIn then
		return
	end
	
	if source.title.value ~= source.publishedIn.value then
		if not source.title.fromLabel then
			source.volumeTitle = source.title
		end
		source.title = {
			value = source.publishedIn.value,
			lang = source.publishedIn.lang,
			fromLabel = source.publishedIn.fromLabel,
			retrieved = source.publishedIn.retrieved,
		}
	end
end

local function removeDuplicateSeries(source)
	if not source.series then
		return
	end
	if source.publishedIn and source.series.entity and source.series.entity == source.publishedIn.entity then
		source.series = nil
	end
end

local function fillPublishedInVersion(source)
	if source.publishedInVersion or not source.publishedIn or not source.publishedIn.entity then
		return
	end
	source.publishedInVersion = { entity = source.publishedIn.entity }
end

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

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

	f:fetch(source, substMap)

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

	fetchUrl(f, source)
	return source, f.lang
end

return p