模块:Common/SMW

来自决策链云智库

local commonSMW = {}

local common = require( 'Module:Common' )
local libraryUtil = require( 'libraryUtil' )
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti


--- Adds SMW properties to a table either from the API or Frame arguments
---
--- @param apiData table Data from the Wiki API
--- @param frameArgs table Frame args processed by Module:Arguments
--- @param smwSetObject table The SMW Object that gets passed to mw.smw.set
--- @param translateFn function The function used to translate argument names
--- @param moduleConfig table Table from config.json
--- @param moduleData table Table from data.json
--- @param moduleName string The module name used to retrieve fallback attribute names
--- @return void
function commonSMW.addSmwProperties( apiData, frameArgs, smwSetObject, translateFn, moduleConfig, moduleData, moduleName )
	checkTypeMulti( 'Module:Common/SMW.addSmwProperties', 1, apiData, { 'table', 'nil' } )
    checkType( 'Module:Common/SMW.addSmwProperties', 2, frameArgs, 'table' )
    checkType( 'Module:Common/SMW.addSmwProperties', 3, smwSetObject, 'table' )
    checkType( 'Module:Common/SMW.addSmwProperties', 4, translateFn, 'function' )
    checkType( 'Module:Common/SMW.addSmwProperties', 5, moduleConfig, 'table' )
    checkType( 'Module:Common/SMW.addSmwProperties', 6, moduleData, 'table' )
    checkType( 'Module:Common/SMW.addSmwProperties', 7, moduleName, 'string' )

    local TNT = require( 'Module:Translate' ):new()
    local lang
    if moduleConfig.module_lang then
        lang = mw.getLanguage( moduleConfig.module_lang )
    else
        lang = mw.getContentLanguage()
    end

	--- Retrieve value(s) from the frame
	---
	--- @param datum table An entry from data.smw_data
	--- @param argKey string The key to use as an accessor to frameArgs
	--- @return string|number|table|nil
	local function getFromArgs( datum, argKey )
		local value
		-- Numbered parameters, e.g. URL1, URL2, URL3, etc.
		if datum.type == 'range' and type( datum.max ) == 'number' then
			value = {}

			for i = 1, datum.max do
				local argValue = frameArgs[ argKey .. i ]
				if argValue then table.insert( value, argValue ) end
			end
		-- A "simple" arg
		else
			value = frameArgs[ argKey ]
		end

		return value
	end

	-- Iterate through the list of SMW attributes that shall be filled
	for _, datum in ipairs( moduleData.smw_data ) do
		-- Retrieve the SMW key and from where the data should be pulled
		local smwKey, from
		for key, get_from in pairs( datum ) do
			if string.sub( key, 1, 3 ) == 'SMW' then
				smwKey = key
				from = get_from or {}
			end
		end

		if type( from ) ~= 'table' then
			from = { from }
		end

		-- Iterate the list of data sources in order, later sources override previous ones
		-- I.e. if the list is Frame Args, API; The api will override possible values set from the frame
		for _, key in ipairs( from ) do
			local parts = mw.text.split( key, '_', true )
			local value

			-- Re-assemble keys with multiple '_'
			if #parts > 2 then
				local tmp = parts[ 1 ]
				table.remove( parts, 1 )
				parts = {
					tmp,
					table.concat( parts, '_' )
				}
			end

			mw.logObject( parts, 'Key Parts' )

			-- safeguard check if we have two parts
			if #parts == 2 then
				-- Retrieve data from frameArgs
				if parts[ 1 ] == 'ARG' then
					value = getFromArgs( datum, translateFn( key ) or '<UNSET>' )

					-- Use EN lang as fallback for arg names that are empty
					if value == nil then
						local success, translation = pcall(
							TNT.formatInLanguage,
							'en',
							string.format( 'Module:%s/i18n.json', moduleName ),
							key
                        )
						if success then
							value = getFromArgs( datum, translation )
						end
					end
				-- Retrieve data from API
				elseif parts[ 1 ] == 'API' and apiData ~= nil then
					mw.logObject({
						key_access = parts[2],
						value = apiData:get( parts[ 2 ] )
					})

					value = apiData:get( parts[ 2 ] )
				end
			end

			-- Transform value based on 'format' key
			if value ~= nil then
				if type( value ) ~= 'table' then
					value = { value }
				end

				for index, val in ipairs( value ) do
					-- This should not happen
					--- FIXME: Somehow this is mutating the original self.apiData, not sure why
					if type( val ) == 'table' and datum.type ~= 'table' and datum.type ~= 'minmax' and datum.type ~= 'subobject' and datum.type ~= 'multilingual_text' then
						val = string.format( '!ERROR! Key %s is a table value; please fix', key )
					end

					-- Format number for SMW
					if datum.type == 'number' then
						val = common.formatNum( val )
					-- Multilingual Text, add a suffix
					elseif datum.type == 'multilingual_text' and moduleConfig.smw_multilingual_text == true then
						-- FIXME: This is a temp fix to handle tables in val (d280346 in API), need some clean up
						if type( val ) == 'table' then
							local tmp = {}
							for _, valText in ipairs( val ) do
								valText = string.format( '%s@%s', valText, moduleConfig.module_lang or mw.getContentLanguage():getCode() )
								table.insert( tmp, valText )
							end
							val = tmp
						else
							val = string.format( '%s@%s', val, moduleConfig.module_lang or mw.getContentLanguage():getCode() )
						end
					-- String format
					elseif type( datum.format ) == 'string' then
						if string.find( datum.format, '%', 1, true  ) then
							val = string.format( datum.format, val )
						elseif datum.format == 'ucfirst' then
							val = lang:ucfirst( val )
						elseif datum.format == 'replace-dash' then
							val = string.gsub( val, '%-', ' ' )
						-- Remove part of the value
						elseif datum.format:sub( 1, 6 ) == 'remove' then
							val = tostring( val ):gsub( mw.text.split( datum.format, ':', true )[ 2 ], '' )
						end
					-- Min/Max
					elseif datum.type == 'minmax' then
						val = {
							common.formatNum( val.min ),
							common.formatNum( val.max ),
						}
					-- Subobject
					elseif datum.type == 'subobject' then
						local api = require( 'Module:Common/Api' )
						for _, data in ipairs( value ) do
							local subobject = {}
							data = api.makeAccessSafe( data )
							commonSMW.addSmwProperties( data, {}, subobject, translateFn, moduleConfig, datum, moduleName )
							mw.smw.subobject( subobject )
						end
					end

					table.remove( value, index )
					table.insert( value, index, val )
				end

				if datum.type ~= 'subobject' and type( smwKey ) == 'string' then
					if type( value ) == 'table' and #value == 1 then
						value = value[ 1 ]
					end

					smwSetObject[ translateFn( smwKey ) ] = value
				end
			end
		end
	end
end


--- Adds SMW ask properties to the ask object, based on the keys defined in data.json (moduleData)
---
--- @param smwAskObject table The table that gets passed to mw.smw.ask
--- @param translateFn function The translate function used to translate argument names
--- @param moduleConfig table The module config from config.json
--- @param moduleData table The module data from data.json
--- @return void
function commonSMW.addSmwAskProperties( smwAskObject, translateFn, moduleConfig, moduleData )
	checkType( 'Module:Common/SMW.addSmwAskProperties', 1, smwAskObject, 'table' )
	checkType( 'Module:Common/SMW.addSmwAskProperties', 2, translateFn, 'function' )
	checkType( 'Module:Common/SMW.addSmwAskProperties', 3, moduleConfig, 'table' )
	checkType( 'Module:Common/SMW.addSmwAskProperties', 4, moduleData, 'table' )

	local langSuffix = ''
	if moduleConfig.smw_multilingual_text == true then
		langSuffix = '+lang=' .. ( moduleConfig.module_lang or mw.getContentLanguage():getCode() )
	end

	for _, queryPart in ipairs( moduleData.smw_data ) do
		local smwKey
		for key, _ in pairs( queryPart ) do
			if string.sub( key, 1, 3 ) == 'SMW' then
				smwKey = key
				break
			end
		end

		local formatString = '?%s'

		if queryPart.smw_format then
			formatString = formatString .. queryPart.smw_format
		end

		-- safeguard
		if smwKey ~= nil and translateFn( smwKey ) ~= nil then
			table.insert( smwAskObject, string.format( formatString, translateFn( smwKey ) ) )

			if queryPart.type == 'multilingual_text' then
				table.insert( smwAskObject, langSuffix )
			end
		end
	end
end


return commonSMW