Модуль:External data

Статья
на 29 ноября 2024 ([ источник])

full_url

{{#invoke:External data|full_url|url=/index.php?do=authors&author=637|base=https://www.apn.ru/index.php}} даст https://www.apn.ru/index.php?do=authors&author=637


--[[
	Обёртки над функциями расширения External Data
--]]

-- Dependencies:
local lang = mw.language.getContentLanguage ()
local find, split, gsub, trim
	= mw.ustring.find or string.find, mw.text.split or string.split, mw.ustring.gsub or string.gsub, mw.text.trim
local clone = mw.clone
local new_url, validate = mw.uri.new, mw.uri.validate
local dump, concat = mw.dumpObject, table.concat

-- Функции расширения ExternalData:
local fetch_pf, show_pf, loop_pf = '#get_web_data', '#external_value', '#display_external_table'

-- Слуюебные функции:
local function full_url_lua (url, base)
  	local parsed_url = new_url (url)
  	local parsed_base = new_url (base)
  	parsed_url.protocol = parsed_url.protocol or parsed_base.protocol or 'https'
  	parsed_url.authority = parsed_url.authority or parsed_base.authority or '??'
	return tostring (parsed_url)
end	-- local function full_url_lua (url, base)

-- Подключение к источнику данных:
local function connect (frame, query, format, mappings, cache, stale, encoding)
	local mappings_lines = {}
	for to, from in pairs (mappings) do
		if to and from then
			mappings_lines [#mappings_lines + 1] = to .. '=' .. from
		end
	end	
	local params = {
		format				= format
	  , url					= query .. '\n'
	  , encoding			= encoding
	  , data				= concat (mappings_lines, ',')
	  , ['cache seconds']	= cache and tostring (cache)
	}
	if format == 'XML' or format == 'HTML with XPath' then
		params [#params + 1] = 'use xpath'
	end
	if stale then
		params [#params + 1] = 'use stale cache'
	end
	-- Required by call_parser_function:
	if #params == 0 then
		params [1] = ''
	end
	local connect = frame:callParserFunction (fetch_pf, params)
	if connect and connect ~= '' then
		-- this is an error:
		return false, connect
	else
		-- successful connection:
		return query
	end
end	-- local function connect (frame, query, format, mappings, cache, stale, encoding)
	
local error_token = '^<span class="error">'
		
-- Однократое извлечение данных успешного подключения:
local function fetch (frame, query, format, mappings, cache, stale, encoding)
	local real_path, error = connect (frame, query, format, mappings, cache, stale, encoding)
	if real_path then
		local data = {}
		for key, _ in pairs (mappings) do
			local value = frame:callParserFunction (show_pf, key)
			if value and value ~= '' and not find (value, error_token) then
				data [key] = tonumber (value) or value
			end
		end	-- for key, _ in pairs (mappings)
		return data, real_path, lang:formatDate ('d xg Y', nil, true)
	else
		return nil, nil, lang:formatDate ('d xg Y', nil, true)
			 , error
	end	-- if real_path
end	-- local function fetch (frame, query, format, mappings, cache, stale, encoding)

local function valid_url (url)
	local valid, parsed = pcall (new_url, url)
	return valid and type (parsed) == 'table' and validate (parsed) and (parsed.host or parsed.path or parsed.query)
end

-- Множественное извлечение данных успешного подключения:
local function fetch_all (frame, query, format, mappings, cache, stale, template, next, encoding)
	if next and trim (next) ~= '' then
		mappings.next = next
	end
	local real_path, error = connect (frame, query, format, mappings, cache, stale, encoding)
	if real_path then
		local params = {}
		for key, _ in pairs (mappings) do
			params [#params + 1] = key .. '=' .. key
		end
		local separator = '(-*-)'
		local pf_params = {''
		  , template	= template
		  , data		= concat (params, ',')
		  , delimiter	= separator
		}
		local glued = frame:callParserFunction (loop_pf, pf_params)
		if glued and glued ~= '' and not find (glued, error_token) then
			local next_url = nil
			if next and trim (next) ~= '' then
				next_url = frame:callParserFunction (show_pf, 'next')
				if not valid_url (next_url) or find (next_url, error_token) then
					next_url = nil
				end
			end
			-- Normal return:
			return split (glued, separator, true), real_path, lang:formatDate ('d xg Y', nil, true), nil, next_url
		else
			error = (error or '')
		end
	end	-- if real_path
	return nil, nil, lang:formatDate ('d xg Y', nil, true), error -- failed.
end	-- local function fetch_all (frame, query, format, mappings, cache, stale, template, next, encoding)

local function scrape_links (frame)
  	local args = clone (frame.args)
  	local mappings = {}
  	for key, val in pairs (args) do
  		if not (key == 'url' or key == 'next' or key == 'format' or key == 'cache'
  			 or key == 'stale' or key == 'template' or key == 'encoding')
  		and val and trim (val) ~= '' then
  			mappings [key] = val
  		end
  	end
  	local next, visited, lines = args.url, {}, {}
  	local first, time
  	local pages, limit = 0, 50
  	while next and not visited [next] and pages <= limit do
  		local current, path, new_lines, error = next
	  	new_lines, path, time, error, next = fetch_all (
	  		frame
	  	  , full_url_lua (next, args.url)
	  	  , args.format
	  	  , mappings
	  	  , args.cache
	  	  , args.stale
	  	  , args.template
	  	  , args.next
	  	  , args.encoding
		)
	  	first = path or first
	  	if not error then
	  		pages = pages + 1
	  		for _, line in ipairs (new_lines) do
	  			lines [#lines + 1] = line
	  		end
	  	else
	  		lines [#lines + 1] = error
		end
		visited [current] = true		
	end	-- while next and not visited [next] and pages <= limit
	return frame:preprocess (
		concat (lines)
	 .. '\n{{!}}}\n'
	 .. '<div align="right">на ' .. time 
	 .. " ''([" .. gsub ((first or ''), '|' , '{{!}}') .. " источник])''</div>\n"
	)
end	-- local function scrape_links (frame)
	
local function full_url (frame)
	return full_url_lua (frame.args.url, frame.args.base)
end

local function trim (frame)	
	return tostring (frame.args.str)
end

return {
	fetch			= fetch
  , fetch_all		= fetch_all
  , scrape_links	= scrape_links
  , full_url		= full_url
  , trim			= trim
}