Модуль:Объекты

Материал из Справочника наблюдателя
Перейти к:навигация, поиск

(i)      Описание модуля[]
Var

Модуль содержит функцию Data, предназначенную для обработки данных об отдельном объекте.

Ключевые параметры
тип = ( УИК / Власти / Сервисы )
формат = ( список колонок / карта / карта kml / карта gpx ) — формат выдачи (таблица, параметры для multimaps, информация в файлы kml и gpx)
url = название страницы — название страницы, в которой расположено обращение к функции Data.
Описания объектов
Описание каждого объекта состоит из последовательности (неключевых с точки зрения синтаксиса MediaWiki) параметров, каждый из которых имеет вид «имя : значение», объекты разделяются параметром «-», например:
|-
  | номер : 2637	
  | МО : Реутов	
  | округ : 18	
  | адрес : ул. Победы, д. 32 — Школа № 2:ул. Победы, д. 32:55.7683325457,37.847943306	
  | телефон : {{T|(498) 661-96-75}}	
  | адрес офиса : 	
  | телефон офиса : {{T|(495) 528-50-53}}	
  | границы : ул. Победы: 22к3, 30; ул. Некрасова: 2, 4, 6, 10, 12, 16	
  | численность : 1692	
  | ПРГ : 	
  | состав : 	
  | КОИБ : 	
  | временный : 	
  | галерея : 
|-

Модуль:Объекты
Модуль:Объекты/doc

Реальные таблицы:

Чехов (Московская область), 16.02.2014 УИК Власти
Заборье (Рязанский район Рязанской области), 16.02.2014 УИК Власти
Кашира, 02.03.2014 УИК Власти
Киржач, 23.03.2014 УИК Власти





Доделать:

  • точки с запятой в адресе убийственны для multimaps (см. УИК 1083 в Твери)
  • улучшить диагностику ошибок (считать порядковый номер участка и номер параметра внутри участка)

local p = {}

local tab = {}

function p.Data (frame)
	local addrOld  -- true, если непременно старый
	
-- Разбор параметров
	local objT = mw.ustring.lower(v(frame.args['тип']))
	if objT == '' then
		objT = 'уик'
	end
	local outF = mw.ustring.lower(v (frame.args['формат']))
	if outF =='{{{1}}}' then -- на самой странице. Проверяем качество таблицы
		local args = frame.args
		local report = ''
		local numnam = ''
			
		for key,val in pairs (args) do
			if type (key) == 'string' then
				if key == 'тип' or key == 'формат' or key == 'url' or key == 'регион' then
				elseif mw.ustring.sub (key,1,4) == 'para' then
					if 	mw.ustring.sub (key,1,5) ~= 'para:' or mw.ustring.sub (key,1,6) == 'para: ' then
						report = report .. '<br />Ключевой параметр: «<code>' .. key  .. ' = ...</code>»' .. ' не должен содержать пробелы вокруг двоеточия'
					end					
				else
					report = report .. '<br />Не экранирован знак равенства: «<code><nowiki>' .. key  .. '</nowiki><span style="color:red">=</span><nowiki>' .. val .. '</nowiki></code>»'
				end
			else
				local mykey, myval = mw.ustring.match (val, '^([^:]+)%:[\t\r\n\f ]*(.*)$')
				if mykey then
					mykey = mw.text.trim (mykey)
					if mykey == 'para' then
						myval = mw.text.trim (myval)
						if args ['para:'..myval] then
							local mykeyref, myvalref = mw.ustring.match (args ['para:'..myval], '^([^:]+)%:[\t\r\n\f ]*(.*)$')
							if not mykeyref then
								report = report .. '<br />Не удалось разобрать ключевой параметр «<code>para:' .. myval  .. '</code>» (объект ' .. numnam .. '): «<code><nowiki>' ..  args ['para:'..myval] .. '</nowiki></code>»'
							else 
								mykey, myval = mykeyref, myvalref
							end
						else
							report = report .. '<br />Несуществующая ссылка «<code>para:' .. myval  .. '</code>» (объект ' .. numnam .. ')'
						end
					end
							
					if (mykey == 'номер') or (mykey == 'наименование') then
						numnam = mw.text.trim (v(myval))
					elseif mykey == 'адрес' or mykey == 'адрес офиса' then
						if mw.ustring.find (myval, ';', 1, true) then
							report = report .. '<br />Обнаружен недопустимый символ «;» в адресе: «<code><nowiki>' .. val .. '</nowiki></code>» (объект ' .. numnam .. ')'
						end
					end
				end
				
			end
		end
		if report ~= '' then
			return frame:preprocess ("<br />{{Box|type=serious|text='''Обнаружены ошибки в данных об объектах!'''" .. report 
				.. '|text-small=Не допускаются точки с запятой в параметре «адрес». Знаки равенства должны быть экранированы двойными фигурными скобками: «<nowiki>{{=}}</nowiki>»}}')
		end
		
	
	end
		
	if (outF == '') or (outF =='{{{1}}}') then
		outF = ({['уик']='номер,округ,адрес,телефон,границы',
				['власти']='тип,наименование,адрес,телефон,сайты,примечание',
				['сервисы']='тип,наименование,адрес,телефон,сайты,примечание',
				}) [objT]
	end
	local url = mw.ustring.lower(v(frame.args['url']))

	tabInit ()

-- Формирование заголовков
	local result = {}
	local columns, geoTemplate
	local csvColName, csvColNumber, csvColCount = {}, {}, {} -- заполняем всегда
	
	if mw.ustring.sub(outF,1,5) == 'карта' then
		if mw.ustring.sub(outF,-3) == 'kml' then
			geoTemplate = '{{ геокарта kml|'
		elseif mw.ustring.sub(outF,-3) == 'gpx' then
			geoTemplate = '{{ геокарта gpx|'
		elseif mw.ustring.sub(outF,-4) == 'json' then
			geoTemplate = '{{ геокарта json|'
		else
			geoTemplate = '{{ multimaps |'
		end
	elseif outF == 'csv' then
--		csvColName, csvColNumber, csvColCount = {}, {}, {}
	else
		columns = mw.text.split (outF, '[\t\r\n\f ]*[,;][\t\r\n\f ]*')
		local colStyle, colText, colName
		local header = ''
		for column = 1, #columns do
				colName = columns[column]
				if tab.headerText [colName] then
						colText = tab.headerText [colName]
						colStyle = tab.headerStyle [colName]
				else
						colText = '«'..v(colname)..'»'
						colStyle = 'style="background-color:red;"'
				end
				header = header .. '<th ' .. colStyle .. '>' .. colText .. '</th>'
		end
		table.insert (result, header)
	end
	
-- Идем по объектам
	local info 
	local infoIdx = 1 -- номер выбираемого параметра (аргумента шаблона)
	
	repeat
		local data = {}
		local dataYes
		repeat
			info, infoIdx = frame.args [infoIdx], infoIdx+1;
			if info then
				info = mw.text.trim (info)
				
				if (info ~= '') and  (info ~= '-') then
					local key, val = mw.ustring.match (info, '^([^:]+)%:[\t\r\n\f ]*(.*)$')
					key = mw.text.trim (v(key))
					if key == 'para' then
						key, val = mw.ustring.match (frame.args ['para:'..val], '^([^:]+)%:[\t\r\n\f ]*(.*)$')
						key = mw.text.trim (v(key))
					end	
					key = mw.ustring.lower(key)
					if key ~= '' then
						val = mw.text.trim (val)
						if val ~= '' then
							data [key] = val
							dataYes = true
						end
					else
						return 'Некорректные данные «' .. info .. '», параметр ' .. (infoIdx-1)
					end
					if csvColNumber[key] then
						if val ~= '' then
							csvColCount[key] = csvColCount[key] + 1
						end
					else
						table.insert (csvColName, key)
						csvColNumber[key] = #csvColName
						if val ~= '' then
							csvColCount[key] = 1
						else
							csvColCount[key] = 0
						end
					end
				end
			end
			
		until not info or info == '-'
				
		if dataYes then
--	перерабатываем адреса
			if ne(data ['адрес']) then
				data ['адрес'] = addrSplit (data ['адрес'])
			end
			if ne(data ['адрес офиса']) then
				data ['адрес офиса'] = addrSplit (data ['адрес офиса'])
			end
			
			
			if columns then		-- таблица
				local res = ''
				for column = 1, #columns do
					local colName = columns[column]
					local dataT, dataColumn = tab.dataType[colName], data[colName]
					if not dataT then
						if not dataColumn then
							dataColumn = ''
						end
					elseif type(dataT) == 'function' then
						dataColumn = dataT (dataColumn, data)
					else
						dataColumn = ''
					end
					res = res .. '<td ' .. v(tab.dataStyle[colName]) .. '>' .. dataColumn .. '</td>'
				end
				table.insert(result,res)
			elseif outF == 'csv' then
				table.insert(result,data) -- А не по ссылке ли здесь всё получится?
			else
-- карты. УИК
				if data ['адрес'][7] ~= 'н' then
					if objT == 'уик' then
						local numb = mw.ustring.sub ('0000' .. normUIK (data['номер']),-4)
						table.insert (result, geoTemplate .. 'УИК|УИК ' .. data['номер'] .. '|' .. bQ(data ['адрес'][1]) .. '|' .. data ['адрес'][2] .. '|' .. data ['адрес'] [3] .. '|' .. data ['адрес'] [4] 
													.. '|/E/' .. string.sub (numb,1,1) .. '/uik' .. numb .. '-e.png|' .. data ['адрес'][7] .. '}}')
	--					objN = objN +1
					else
						local tipus = v(data['тип'])
						local icon = tab.icoName [tipus]
						if icon then
							icon = '|/E/gov/' .. icon .. '-e.png'
						else
							icon = '|'
						end
						table.insert (result, geoTemplate .. (data['метка'] or tipus) .. '|' .. v (data['наименование']) .. '|' .. bQ(data ['адрес'][1]) .. '|' .. data ['адрес'][2] .. '|' .. data ['адрес'] [3] .. '|' .. data ['адрес'] [4] 
													.. icon .. '|' .. data ['адрес'][7] .. '}}')
	--					objN = objN +1
					end	
				end
			end
		end
	until not info;
	
-- завершающие операции	
	
	
	
	
	
	if columns then
		if url ~= '' then
			url ='<caption class=plainlinks style="font-size:80%; line-height:110%; text-align:right;">' 
			.. 'Сведения об объектах вносятся '
			.. ' [{{fullurl:{{{url|{{FULLPAGENAME}}/{{{тип|УИК}}}}}}|action=edit}} здесь]. '
			.. 'Скачать [//www.nablawiki.ru/images/ArticleToFile/{{urlencode:{{BASEPAGENAME}} - {{{тип|УИК}}}.csv|PATH}} файл в формате csv]'
			.. '([[Как работать с таблицей объектов в формате csv|?]])</caption>'
		end
		return frame:preprocess ('<table  class="wikitable sortable">' .. url 
			.. '<tr>' .. table.concat (result, '</tr><tr>') .. '</tr></table>')
	elseif outF == 'csv' then
		local res = ''
		for j = 1, #csvColName do
			if csvColCount[csvColName[j]] ~= 0 then
				if csvColName[j] == 'адрес' or csvColName[j] == 'адрес офиса' then
					if addrOld then
						res = res .. '"' .. csvColName[j] .. ' (текст)","' .. csvColName[j] .. ' (дом)","' .. csvColName[j] .. ' (гео)","' .. csvColName[j] .. ' (вид)",'
					else
						res = res .. '"' .. csvColName[j] .. ' (дом)","' .. csvColName[j] .. ' (орг)","' .. csvColName[j] .. ' (гео)","' .. csvColName[j] .. ' (вид)",'
					end
--				elseif csvColName[j] == 'телефон' or csvColName[j] == 'телефон офиса' then
--					res = res .. '"' .. csvColName[j] .. '",'
				else
					res = res .. '"' .. csvColName[j] .. '",'
				end
			end
		end
		res = res .. '\n'
		for i = 1, #result do
			for j = 1, #csvColName do
				if csvColCount[csvColName[j]] ~= 0 then
					if result[i][csvColName[j]] then
						if csvColName[j] == 'адрес' or csvColName[j] == 'адрес офиса' then
							local aa
							if addrOld then
								aa = 2
								res = res .. '"' .. dQ(result[i][csvColName[j]] [1]) .. '","' .. dQ(result[i][csvColName[j]] [2]) .. '",' 
--									.. result[i][csvColName[j]] [3] .. ',' .. result[i][csvColName[j]] [4] .. '",'
							else
								aa = 5
								res = res .. '"' .. dQ(result[i][csvColName[j]] [5]) .. '","' .. dQ(result[i][csvColName[j]] [6]) .. '",' 
--									.. result[i][csvColName[j]] [3] .. ',' .. result[i][csvColName[j]] [4] .. '",'
							end	
							if result[i][csvColName[j]] [3] ~= '' then
								res = res .. '"' .. result[i][csvColName[j]] [3] .. ',' .. result[i][csvColName[j]] [4] .. '",'
							else
								res = res .. '"' .. '{{#yandexgeo:{{#var:Геобаза}}, ' .. result[i][csvColName[j]] [aa] .. '|maps}}' .. '",'
							end
							if result[i][csvColName[j]] [7] ~= 'д' then
								res = res .. '"' .. result[i][csvColName[j]] [7] .. '",'
							else
								res = res .. '"",'
							end
							
						else
							res = res .. '"' .. dQ(result[i][csvColName[j]]) .. '",'
						end
					else
						if csvColName[j] == 'адрес' or csvColName[j] == 'адрес офиса' then
							res = res .. '"","","","",'
						else
							res = res .. '"",'
						end
					end
				end
			end
			res = res .. '\n'
		end
		return frame:preprocess (res)
					
				
	else
		return frame:preprocess (table.concat (result))
	end
	
end


function addrSplit (addr)
	local a = mw.text.split (addr..'::', '[\t\r\n\f ]*:[\t\r\n\f ]*')
	local x = {}
	if a[1] == '' then --новый формат
		x = { a[2] or '', a[3] or '', a[4] or ''}
		if x[2] == '' then
			a[1] = x[1]
		else	
			a[1] = x[1] .. ' — ' .. x[2] 
		end
		a[3] = x[3]
		a[4] = a[5]
	elseif e (a[2]) then
		x[1] = a[1]
		x[2] = ''
	else
		if mw.ustring.sub (a[1],1,mw.ustring.len(a[2])) == a[2] then
			x[1] = a[2]
			x[2] = mw.text.trim(mw.ustring.sub (a[1],mw.ustring.len(a[2])+1,-1))
			if mw.ustring.sub (x[2], 1, 2) == '— ' then
				x[2] = mw.text.trim (mw.ustring.sub (x[2], 3, -1)) 
			end
			
		else
			addrOld = true
			a[1] = '! ' .. a[1]
			x[1] = ''
			x[2] = ''
		end
	end
	local c = {'', ''}
	if ne(a[3]) then
		c = mw.text.split (a[3]..',', '[\t\r\n\f ]*,[\t\r\n\f ]*')
	end
	return { a[1], a[2], c[1], c[2], x[1], x[2], mw.ustring.lower (mw.ustring.sub ((a[4] or '') .. 'да',1,1)) }
end

function e (t)
	return (t or '') == ''
end

function ne (t)
	return (t or '') ~= ''
end

function v (t)
	return (t or '')
end

function normUIK (numbUIK)
	return ( string.gsub (numbUIK, '^0*([0-9]+)[%-/]?([0-9]*)$','%1%2'))..''
end

function dQ (s)
	return mw.ustring.gsub (s, '"', '""')
end

function bQ (s)
	return mw.ustring.gsub (s, '"', '\\"')
end

function tabInit ()
	tab.headerStyle = {
		['номер']          = 'style="width:4en;"';
		['мо']             = '';
		['округ']          = 'style="width:3en;"';
		['коиб']           = 'style="width:2en;"';
		['временный']      = 'style="width:2en;"';
		['численность']    = 'style="width:6en;"';
		['прг']            = '';
		['адрес']      	   = 'class=unsortable';
		['телефон']        = 'class=unsortable';
		['адрес офиса']    = 'class=unsortable';
		['телефон офиса']  = 'class=unsortable';
		['границы']        = 'class=unsortable';
		['номер0412']      = 'style="width:4en; font-size:75%; line-height:80%"';
		['итог0412']       = 'style="width:4en; font-size:75%; line-height:80%"';
		['явка0412']       = 'style="width:4en; font-size:75%; line-height:80%"';
		['дом0412']        = 'style="width:4en; font-size:75%; line-height:80%"';
		['числ0412']       = 'style="width:4en; font-size:75%; line-height:80%"';
		['номер0403']      = 'style="width:4en; font-size:75%; line-height:80%"';
		['итог0403']       = 'style="width:4en; font-size:75%; line-height:80%"';
		['явка0403']       = 'style="width:4en; font-size:75%; line-height:80%"';
		['дом0403']        = 'style="width:4en; font-size:75%; line-height:80%"';
		['числ0403']       = 'style="width:4en; font-size:75%; line-height:80%"';
		['галерея']        = 'style="width:2en;"';
		['тип']            = 'style="width:3en; border-right:0px;"';
		['наименование']   = '';
		['метка']          = '';
		['сайты']          = 'class=unsortable';
		['примечание']     = 'class=unsortable';
		['#явка']          = '';
		['#досрочно']      = '';
		['#дома']          = '';
		['#власть']        = '';
		['#явка%']         = '';
		['#досрочно%']     = '';
		['#дома%']         = '';
		['#власть%']       = '';
	}
	 
	tab.headerText = {
		['номер']          = 'УИK';
		['мо']             = 'МО';
		['округ']          = '<small>округ</small>';
		['коиб']           = '<small>KОИБ</small>';
		['временный']      = '<small>{{подсказка|вр.|участок, созданный для временно пребывающих}}</small>';
		['численность']    = 'изб.';
		['прг']            = '{{подсказка|ПPГ|число членов комиссии с правом решающего голоса}}';
		['адрес']          = 'адрес помещения';
		['телефон']        = 'телефон';
		['адрес офиса']    = 'адрес офиса';
		['телефон офиса']  = 'тел. офиса';
		['границы']        = 'границы избирательного участка';
		['номер0412']      = '04.12<br />№ УИК';
		['итог0412']       = '04.12<br />за ЕР';
		['явка0412']       = '04.12<br />явка';
		['дом0412']        = '04.12<br />дома';
		['числ0412']       = '04.12<br />число';
		['номер0403']      = '04.03<br />№ УИК';
		['итог0403']       = '04.03<br />Путин';
		['явка0403']       = '04.03<br />явка';
		['дом0403']        = '04.03<br />дома';
		['числ0403']       = '04.03<br />число';
		['галерея']        = '<small>фото</small>';
		['тип']            = ' ';
		['наименование']   = 'наименование органа';
		['метка']          = 'сокр.';
		['сайты']          = 'сайт';
		['примечание']     = 'примечание';
		['#явка']          = 'явка';
		['#досрочно']      = 'доср.';
		['#дома']          = 'дома';
		['#власть']        = 'власть';
		['#явка%']         = 'явка';
		['#досрочно%']     = 'доср.';
		['#дома%']         = 'дома';
		['#власть%']       = 'власть';
	}
	tab.dataStyle = {
		['номер']          = 'class=plainlinks style="text-align:center; font-weight:bold; background-color:#f2f2f2;"';
		['мо']             = 'style="font-size:83%; line-height:95%;"';
		['округ']          = 'style="text-align:center; "';
		['коиб']           = 'style="text-align:center; "';
		['временный']      = 'style="text-align:center; "';
		['численность']    = 'style="text-align:right; background-color:Beige;"';
		['прг']            = 'style="text-align:center; "';
		['адрес']          = 'style="font-size:92%; line-height:95%;"';
		['телефон']        = '';
		['адрес офиса']    = 'style="font-size:92%; line-height:95%;"';
		['телефон офиса']  = '';
		['границы']        = 'style="font-size:83%; line-height:95%;"';
		['номер0412']      = 'style="text-align:center; font-weight:bold; background-color:#f2f2f2;"';
		['итог0412']       = 'style="text-align:right; "';
		['явка0412']       = 'style="text-align:right; background-color:Ivory; "';
		['дом0412']        = 'style="text-align:right; background-color:Honeydew; "';
		['числ0412']       = 'style="text-align:right;  background-color:Beige;"';
		['номер0403']      = 'style="text-align:center; font-weight:bold; background-color:#f2f2f2;"';
		['итог0403']       = 'style="text-align:right; "';
		['явка0403']       = 'style="text-align:right; background-color:Ivory; "';
		['дом0403']        = 'style="text-align:right; background-color:Honeydew; "';
		['числ0403']       = 'style="text-align:right;  background-color:Beige;"';
		['галерея']        = '';
		['тип']            = 'style="border-right:0px;"';
		['наименование']   = '';
		['метка']          = '';
		['сайты']          = 'style="font-size:83%; line-height:95%;"';
		['примечание']     = 'style="font-size:83%; line-height:95%;"';
		['#явка']          = 'style="text-align:right; background-color:Ivory; "';
		['#досрочно']      = 'style="text-align:right; background-color:Ivory; "';
		['#дома']          = 'style="text-align:right; background-color:Ivory; "';
		['#власть']        = 'style="text-align:right; background-color:Ivory; "';
		['#явка%']         = 'style="text-align:right; background-color:Ivory; "';
		['#досрочно%']     = 'style="text-align:right; background-color:Ivory; "';
		['#дома%']         = 'style="text-align:right; background-color:Ivory; "';
		['#власть%']       = 'style="text-align:right; background-color:Ivory; "';
	}
	tab.icoName = {
		['ИК']          = 'ik';
		['ТИК']         = 'tik';
		['ИКМО']        = 'ikmo';
		['полиция']     = 'ovd';
		['прокуратура'] = 'proc';
		['СК']          = 'ck';
		['суд']         = 'sud2';
		['адм']         = 'adm2';
	}	

	
	tab.dataType = {}
	tab.dataType['галерея']=function (s)
		if (not s) or (s == '') then
			return ''
		else
			return '[[Файл:Фотофайлы.png|link=' .. s .. ']]'
		end
	end

	tab.dataType['номер']=function (s,column)
		if (not s) or (s == '') then
			return ''
		else
			local txt = '[https://www.wikiuiki.org/ik/{{#var:RegCode}}-uik-' .. normUIK (s) .. ' ' .. s .. ']';
			-- [{{#invoke:Campaign|RegGASnew|{{#if:{{{регион|}}}|{{{регион}}}|{{PAGENAME}}}}}}/ik ТИКи]
			if (column ['гас'] or '') ~= '' then
				txt = txt .. ' [' .. column ['гас'] .. ' <sup><span style="color:red">§</span></sup>]';
			end
			return txt
		end
	end

	tab.dataType['адрес']=function (s)
		if (not s) or (s == '') then
			return ''
		else
			return '{{геоадрес|' .. table.concat(s,'|') .. '}}';
		end
	end
	tab.dataType['адрес офиса']=function (s)
		if (not s) or (s == '') then
			return ''
		else
			return '{{геоадрес|' .. table.concat(s,'|') .. '}}';
		end
	end
	tab.dataType['#явка%']=function (s, column)
		return (string.format('%4.1f', tonumber (column ['#явка']) / tonumber (column ['численность']) * 100) .. '%');
	end
	tab.dataType['#досрочно%']=function (s, column)
		return (string.format('%4.1f', tonumber (column ['#досрочно']) / tonumber (column ['численность']) * 100) .. '%');
	end
	tab.dataType['#дома%']=function (s, column)
		return (string.format('%4.1f', tonumber (column ['#дома']) / tonumber (column ['численность']) * 100) .. '%');
	end
	tab.dataType['#власть%']=function (s, column)
		return (string.format('%4.1f', tonumber (column ['#власть']) / tonumber (column ['численность']) * 100) .. '%');
	end
	tab.dataType['телефон']=function (s)
		if (not s) or (s == '') then
			return ''
		elseif mw.ustring.sub(s,1,1) == '<' then
			return s
		else
			return '{{T|' .. s .. '}}'
		end
	end
	tab.dataType['телефон офиса']=function (s)
		if (not s) or (s == '') then
			return ''
		elseif mw.ustring.sub(s,1,1) == '<' then
			return s
		else
			return '{{T|' .. s .. '}}'
		end
	end
	
end		



return p