Модуль:ФИО: различия между версиями

Материал из Кадровый состав НКВД 1935-1939
Перейти к навигации Перейти к поиску
Нет описания правки
Нет описания правки
Строка 117: Строка 117:
local im2 = string.find( tmp .. ' ', ' ', 1, true )
local im2 = string.find( tmp .. ' ', ' ', 1, true )
local im1 = string.sub( tmp, 1, im2 - 1 )
local im1 = string.sub( tmp, 1, im2 - 1 )
im2 = string.sub( tmp, im2+1, -1)
im2 = mw.text.trim( string.sub( tmp, im2+1, -1) ) -- если отчества не было, а имя двойное, то может быть пробел в конце
local query = tools.cq(  
local query = tools.cq(  

Версия от 14:13, 1 марта 2017

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

local p = {}
local global = mw.ext.luaglobal;
local tools = require( 'Модуль:Tools' )
global.strict( true )

local argus = { true, true, true, true, ['дополнение'] = '', ['сноска'] = '', ['источник'] = '', }
p[''] = function ( frame )
	local args, tmp = tools.checkargs( frame:getParent().args, argus )
	local ourPage = mw.title.getCurrentTitle().text
	local errorAdvice = 'Используйте формат {{ ФИО | <var>Фамилия</var> | '
		.. '<var>Имя</var> | <var>Отчество</var>  | '
		.. '<var>номер тезки в скобках при наличии</var> | '
		.. 'дополнение = <var>текст</var> | сноска =  <var>текст</var> | '
		.. 'источник = <var>текст</var> }}'
    if not args then
		return tools.errorMsg( 'ФИО', 'Неизвестный параметр ' .. tmp, errorAdvice )
    end
	local fa, im, ot, idx
	fa = args[1]
	if not fa then
		return tools.errorMsg( 'ФИО', 'Не указана фамилия', errorAdvice )
	end

	-- изменения: 
	-- * индекс обязателен даже у первого вызова (чтобы при переименованиях не потерялось)
	-- * Вариант: {ф и о|индекс} убираем за бессмысленностью. Либо единое поле, либо поэлементно
	
	if args[2] == '-' then -- старый способ указания корейца
		fa = fa .. ', '
		args[2] = nil
	end
	-- Если у нас единое поле, то надо разбить на fa-im-ot-idx
	if #args == 1 then
		-- прежде всего делим по запятой: до запятой фамилия
		tmp = mw.text.split( fa .. ' ', ', ' ) -- пробел добавили для концевой запятой
		if #tmp > 2 then
			return tools.errorMsg( 'ФИО',
				'Запятая используется только для отделения фамилии от имени-отчества',
				errorAdvice )
		end
		tmp[#tmp] = mw.text.trim( tmp[#tmp] )
		if #tmp == 2 then
			fa = tmp[1]
			tmp = mw.text.split( tmp[2], ' ' )
		else
			tmp = mw.text.split( tmp[1], ' ' )
			fa = tmp[1]
			table.remove( tmp, 1 )
		end
		
		idx = tmp[#tmp]
		if string.match( idx, '^%([IVX]+%)$' ) then
			tmp[#tmp] = nil
		else
			idx = nil
		end
		
		if #tmp > 2 then
			return tools.errorMsg( 'ФИО',
				'Если какая-то часть ФИО содержит несколько слов, разделяйте части символом «|» ' .. (fa or '—') .. '\n' .. mw.dumpObject(tmp),
				errorAdvice )
		end
		im, ot = tmp[1], tmp[2]
		if im == '' then
			im = nil
		end
	else
		im, ot, idx = args[2], args[3], args[4]
		if idx and not string.match( idx, '%([IVX]+%)' ) then
			return tools.errorMsg( 'ФИО',
				'Номер тезки должен указываться в скобках римскими цифрами',
				errorAdvice )
		end
	end
	
	-- здесь у нас fa-im-ot-idx. Запятой нет, idx корректен, в остальном ФИО не проверено
	-- Проверка корректности
	
	
	local gFIO = global.get( 'gFIO' )
	if not gFIO then
		-- Перед дальнейшими преобразованиями сверимся с заголовком страницы
		tmp = { fa .. ',' }
		tmp[#tmp+1] = im
		tmp[#tmp+1] = ot
		tmp[#tmp+1] = idx
		local tmp1 = table.concat( tmp, ' ' )
		if not im and not ot then
			tmp[1] = fa
			tmp = table.concat( tmp, ' ' )
		else
			tmp = nil
		end
		if ourPage ~= tmp1 and ourPage ~= tmp then
			return tools.errorMsg( 'ФИО',
				'Первый вызов шаблона ФИО должен соответствовать заголовку страницы',
				errorAdvice )
		end
		frame:callParserFunction{ name = 'DISPLAYTITLE',
			args = tools.name( ourPage ) }
	end
	ot = ot or ''
	im = im or ''
	-- поверхностная проверка имени
	if not mw.ustring.match( fa..im..ot, '^[А-Яа-я%- %.]+$' ) then
		return tools.errorMsg( 'ФИО',
			'Имя может содержать только русские буквы',
			errorAdvice )
	end
	--@TODO  проверка на кызы-оглы-заде, (нужна для псевдонимов, даже если она есть в newarticletext)
	
	tmp = im .. ' ' .. ot
	tmp = string.gsub( tmp, '%-%.', '' )
	tmp = string.gsub( tmp, '%.%.%.', '' )
	tmp = string.gsub( tmp, '%.', '' )
	--@TODO заменить Ё на Е
	local im2 = string.find( tmp .. ' ', ' ', 1, true )
	local im1 = string.sub( tmp, 1, im2 - 1 )
	im2 = mw.text.trim( string.sub( tmp, im2+1, -1) ) -- если отчества не было, а имя двойное, то может быть пробел в конце
	
	local query = tools.cq( 
			'names_table', 
			'_pageName, lastN, firstN, middleN, disambig',
			{ 
				where = 'lastN="' .. fa .. '" AND firstN="' .. im1 
					.. '" AND middleN="' .. im2 .. '"',
			}
		)	
	local namesakes = {}
	for _, row in pairs( query ) do
		namesakes[row.disambig or '' ] = row
	end
	tmp = namesakes[idx or '']
	if tmp and tmp._pageName ~= ourPage then
		return tools.errorMsg( 'ФИО',
			'Указанное имя персоналии относится к [[' .. tmp._pageName 
				.. '|другому лицу]]',
			errorAdvice )
	end
	if idx and namesakes[''] then
		return tools.errorMsg( 'ФИО',
			'При наличии тезок [[' .. namesakes['']._pageName 
				.. '|соответствующее имя без указания номера]]'
				.. ' должно относиться к странице разрешения неоднозначности',
			errorAdvice )
	end
	if not idx and ( #query > 1 or ( #query == 1 and query[1].disamig  ) ) then
		return tools.errorMsg( 'ФИО',
			'Указанное имя уже использовалось на других страницах с индексами.'
				..	' Поэтому без индекса его можно использовать только для страницы разрешения неоднозначности',
			errorAdvice )
	end
		

	if not gFIO then
		gFIO = {}
	end
	table.insert( gFIO, { l = fa, i = im, o = ot, d = idx, f = im1, m = im2, 
		a = args['дополнение'], r = args['сноска'], s = args['источник'],  } )
	global.set( 'gFIO', gFIO )

	return frame:expandTemplate{ title = 'Cargo:Names', args = {
			lastN     = fa,
			firstN    = im1,
			middleN   = im2,
			disambig  = idx,
			addendum  = args['дополнение'],
			porno     = #gFIO,    --@TODO если это убрать, то можно не проверять на дубликаты
		} } .. '<i class="nkvd-hide-link">[ «' .. fa .. '» | «' 
			.. ( im or '—' ) .. '» : «' .. ( ot or '—' ) .. '» | «' 
			.. ( im1 or '—' ) .. '» : «' .. ( im2 or '—' ).. '» | «' 
			.. ( idx or '—' ) .. '» ] </i>'

		
--[=[
		return '\n\nФИО: ( ' .. ( fa or '—' ) .. ' | ' .. ( im or '—' ) 
			.. ' | ' .. ( ot or '—' ) .. ' | ' .. ( idx or '—' ) .. ' ) >> ( '
			.. ( im1  or '—' ) .. ' | ' .. ( im2 or '—' ) .. ' )\n'
--]=]			

	
	
--[====[	



	-- Варианты: {ф и о} {ф и о|индекс} {ф и о индекс} {ф|и|о} {ф|и|о|индекс}
	local im = args[2]
	local idx = ''
	if string.sub( im, 1, 1 ) == '(' then
		im, idx = '', im
	end
	local ot = args[3]
	tmp = args[4]
	if tmp ~= '' then
		if string.sub( tmp, 1, 1 ) ~= '('  or  idx ~= ''  then
			return tools.errorMsg( 'ФИО', 'Ошибка в номере тезки', errorAdvice )
		end
		idx = tmp
	end
	if im == '' then
		tmp = mw.ustring.find( fa, '(', 1, true )
		if tmp then
			fa, idx = mw.text.trim( mw.ustring.sub( fa, 1, tmp-1 ) ), mw.ustring.sub( fa, tmp, -1 )
		end
		local fio = mw.text.split( fa, '[\t\r\n\f ]+', false )
		if #fio > 3 then
			return tools.errorMsg( 'ФИО',
				'Если какая-то часть ФИО содержит несколько слов, разделяйте части символом «|»',
				errorAdvice )
		end
		if #fio == 1 then
			return tools.errorMsg( 'ФИО', 'Задана только фамилия. Если имени действительно нет, то поставьте вместо него знак «-»', errorAdvice )
		end
		if mw.ustring.sub( fio[1], -1, -1) == ',' then
			fio[1] = mw.ustring.sub( fio[1], 1, -2)
		end
		fa, im, ot = fio[1], fio[2], fio[3]
	end
	if not  mw.ustring.match( fa, '^[А-Яа-яЁё%- ]+$' ) then
		return tools.errorMsg( 'ФИО', 'Недопустимые символы в фамилии', errorAdvice )
	end
	if not ( mw.ustring.match( im, '^[А-Яа-яЁё%- ]+$' )
			or mw.ustring.match( im, '^[А-ЯЁ%-]%.$' ) ) then
		return tools.errorMsg( 'ФИО', 'Недопустимые символы в имени', errorAdvice )
	end
	if not ( mw.ustring.match( ot, '^[А-Яа-яЁё%- ]*$' )
			or mw.ustring.match( ot, '^[А-ЯЁ%-]%.$' ) ) then
		return tools.errorMsg( 'ФИО', 'Недопустимые символы в отчестве', errorAdvice )
	end

	local fio
	if im == '-' then
		fio = fa -- китаец
		im = ''
	else
		fio = mw.text.trim( fa .. ', ' .. im .. ' ' .. ot )
	end

	if not gFIO then
		-- первый шаблон ФИО на странице. Может отличаться от заголовка только
		-- отсутствием индекса тезки (чтобы при переименовании страницы для
		-- добавления индекса не нужно было править содержимое страницы)
		if ourPage ~= fio then
			local pageIdx = mw.ustring.find( ourPage, '(', 1, true )
			if pageIdx then
				pageIdx = mw.ustring.sub( ourPage, pageIdx, -1 )
			else
				return tools.errorMsg( 'ФИО', errorPageName, errorAdvice )
			end
			if idx ~= '' and idx ~= pageIdx then
				return tools.errorMsg( 'ФИО', errorPageName, errorAdvice )
			end
			idx = pageIdx
		end
		if idx ~= '' then
			frame:callParserFunction{ name = 'DISPLAYTITLE',
				args = fio .. ' <span class="nkvd-idx">' .. idx .. '</span>' }
		end

	--@TODO для псевдонимов надо проверять дублирование на самой странице!
	end
--]====]

--[====[
	-- Проверка тезок
	local crA, crB, crC, crD
	--

	local namesakes = {}

--	tmp = frame:callParserFunction{ name = '#cargo_query', args = { '',
--		tables       = 'names',
--		where        = 'name="' .. fio .. '"',
--		fields       = 'disambig, CONCAT(_pageName)=pn',
--		['order_by'] = 'disambig',
--		limit        = '999',
--		format       = 'native',
--		} }
--	local qres = mw.ext.cargo.get()
	local qres = tools.cq( 'names', 'disambig, _pageName', {
		where        = 'name="' .. fio .. '"',
		orderBy      = 'disambig',
		limit        = '999',
	} )

	if qres then
		for _, namesake in ipairs( qres ) do
			local id = namesake.disambig
			namesakes[id] = namesake._pageName
			if id ~= '' and id ~= idx then
				crC = true
			end
		end
	end



	crA = namesakes[''] ~= nil
	crB = ( namesakes[idx] or ourPage ) ~= ourPage
	crD = mw.title.new( fio ).exists
	local actionIdx = 0  -- 1 - укажите индекс, 2 - измените индекс
	local actionAlt = 0  -- 1 - переименуйте страницу, 2 — отредактируйте страницу
	local actionDis = 0  -- 1 — создайте страницу разрешения неоднозначностей
	local debugFio = '(' .. ourPage .. '/' .. fio  .. ':' .. idx .. ':' .. tostring( crA ) .. ' ' .. tostring( crB ) .. ' ' .. tostring( crC ) .. ' ' .. tostring( crD ) .. ') ' .. mw.dumpObject( qres ) .. '\n'
	if idx == '' then
		if     not crB and not crC then
		elseif not crB and     crC then
			actionIdx = 1
			if not crD then
				actionDis = 1
			end
		elseif     crB and not crC then
			if crD then
				actionAlt = 2
			else
				actionAlt = 1
			end
			actionIdx = 1
			actionDis = 1
		else --    crB and     crC then
			error( 'Ошибка алгоритма' )
		end
	elseif not crA and not crB then
		if not crD then
			actionDis = 1
		end
	elseif not crA and crB then
		actionIdx = 2
		if not crD then
			actionDis = 1
		end
	elseif crA and not crB and not crC then
		if crD then
			actionAlt = 2
		else
			actionAlt = 1
		end
		actionDis = 1
	else
		error( 'Ошибка алгоритма' )
	end

	local txtErr,txtAdv = '', ''
	if actionIdx ~= 0 then
		if actionIdx == 1 then
			actionIdx = ''
		else
			actionIdx = 'другой (свободный) '
		end
		if gFIO then
			txtErr = 'изменить вызов шаблона ФИО'
		else
			txtErr = 'переименовать данную страницу'
		end
		txtErr = 'Необходимо ' .. txtErr .. ', указав ' .. actionIdx
			.. '«индекс тезки»'
		if actionDis ~= 0 then
			txtErr = txtErr .. ' — скорее всего, «(II)»'
		end
		if actionAlt ~= 0 then
			txtAdv = 'После этого будет необходимо переименовать/отредактировать страницу тезки'
			if actionDis ~= 0 then
				txtAdv = txtAdv .. ' и создать страницу разрешения неоднозначностей'
			end
		elseif actionDis ~= 0 then
			txtAdv = 'После этого будет необходимо создать страницу разрешения неоднозначностей'
		end
	elseif actionAlt == 2 then
		txtErr = 'Необходимо перейти по этой [[Special:MovePage/' .. fio
			.. '|ссылке]], чтобы переименовать ранее созданную страницу с именем «'
			.. fio ..'» в «' .. fio ..' (I)»'
		txtAdv = 'Страница «' .. fio
			..'» будет использоваться как указатель для разрешения неоднозначности'
	elseif actionAlt == 1 then
		txtErr = 'Необходимо перейти на ['
			.. tostring( mw.uri.fullUrl( namesakes[''], {
				action = 'edit',
				editintro = 'Template:Disambiguation_renumber_editnotice',
				para = fio
				} ) )
			.. ' страницу], где имя «' .. fio ..'» указано в качестве псевдонима'
			.. ' в одном из шаблонов ФИО, чтобы добавить к нему индекс тезки «(I)»'
		if actionDis ~= 0 then
			txtAdv = 'После этого будет необходимо создать страницу разрешения неоднозначностей'
		end
	elseif actionDis ~= 0 then
		txtErr = 'Необходимо перейти по этой ['
			.. tostring( mw.uri.fullUrl( fio, {
				action = 'edit',
				preload = 'Template:Disambiguation_pattern',
				editintro = 'Template:Disambiguation_editnotice'
				} ) )
			.. ' ссылке], чтобы создать страницу разрешения неоднозначностей'
	end
	if txtErr ~= '' then
		return tools.errorMsg ( 'ФИО', txtErr, txtAdv )
			-- .. '\n' .. debugFio )
	end
	local addendum, comment, source =
		args['дополнение'] or '', args['сноска'] or '', args['источник'] or ''
	if not gFIO then
		gFIO = {}
	end
	table.insert( gFIO, { fa, im, ot, idx, addendum, comment, source } )
	global.set( 'gFIO', gFIO )
	-- ставим вызов Cargo в return, чтобы не потерять сообщения об ошибках
	return frame:expandTemplate{ title = 'Таблица ФИО', args = {
			name = fio,
			disambig = idx,
			addendum = addendum,
		} } .. '<i></i>'
--]====]
end

return p