Module:ExchangeDefault

Revision as of 11:48, 6 December 2020 by en>Elessar2 (Protected "Module:ExchangeDefault": High-use template/module: Seems like this should for sure be partially protected since template is ([Edit=Allow only autoconfirmed users] (indefinite) [Move=Allow only administrators] (indefinite)))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:ExchangeDefault/doc

-- <pre>
-- Default view for {{ExchangeItem}} for use on Exchange ns pages
--
-- Use of this via #invoke is deprecated and will eventually be removed
-- This is intended for use by [[Module:Exchange]]
--

local p = {}

-- imports
local exchange = require( 'Module:Exchange ' )
local excgdata = require( 'Module:ExchangeData' )._main
local round = require( 'Module:Number' )._round
local timeago = require( 'Module:TimeAgo' )._ago
local yesno = require( 'Module:Yesno' )

function p.main( item )
    local frame = mw.getCurrentFrame()
    local lang = mw.language.getContentLanguage()
    local title = mw.title.getCurrentTitle()
    local data  = mw.loadData( 'Module:Exchange/' .. item )
	local bulkData = {
		price = exchange.loadBulkData(item, 'price'),
		date = exchange.loadBulkData('%LAST_UPDATE_F%', 'price'),
		last = exchange.loadBulkData(item, 'lastPrice'),
		volume = exchange.loadBulkData(item, 'volume'),
		volumeDate = exchange.loadBulkData('%LAST_UPDATE_F%', 'volume'),
	}
    -- set variables here if possible to keep table building easier to follow
    local rowspan = 8
    local volDate = bulkData.volumeDate or lang:formatDate( 'c' )
    local dateDiffZ = lang:formatDate( 'z' ) - lang:formatDate( 'z', volDate )
    local dateDiffY = lang:formatDate( 'Y' ) - lang:formatDate( 'Y', volDate )
    local price = bulkData.price and lang:formatNum( bulkData.price ) or '<i>Unknown</i>'
    local priceDiff = (bulkData.price or 0) - (bulkData.last or 0)
    local priceDiffF = lang:formatNum(priceDiff) or '<i>Unknown</i>'
    local priceDiffPerc = ''
    local priceDiffClass = 'unchanged' --todo neutral
    local priceDiffSlide = '' --todo neutral
    local priceDiffIcon = 'Unchanged' --todo neutral
    local itemIcon = data.icon or (( data.item or 'Coins 1000') .. '.png')
    local date = '<i>Unknown</i>'
    local lowAlchMultiplier = 0.4
    local highAlchMultiplier = 0.6
    local rawLowAlch = -1
    local rawHighAlch = -1
    local lowAlch = '<i>Not alchable</i>'
    local highAlch = '<i>Not alchable</i>'
    local memsIcon = ''
    local usage = ''

    if data.volume and ( dateDiffZ + 365 * dateDiffY ) <= 7 then
        rowspan = 10
    end

    if bulkData.date then
        date = lang:formatDate( 'j F Y, H:i "(UTC)"', bulkData.date )
        timeagodate = bulkData.date
    end
    
    if data.alchable == nil or yesno( data.alchable ) then
        if data.value then
	    	if data.alchmultiplier then
	    		highAlchMultiplier = data.alchmultiplier
	    		lowAlchMultiplier = highAlchMultiplier * 2/3
	    	end
            rawLowAlch = math.max( math.floor( data.value * lowAlchMultiplier ), 1 )
            rawHighAlch = math.max( math.floor( data.value * highAlchMultiplier ), 1 )
            lowAlch = lang:formatNum( rawLowAlch )
            highAlch = lang:formatNum( rawHighAlch )
        else
            lowAlch, highAlch = '<i>Unknown</i>', '<i>Unknown</i>'
        end
    end

    if data.members ~= nil then
        if data.members then
            memsIcon = 'Members'
        else
            memsIcon = 'Free-to-play'
        end
    end
    
    if priceDiff > 0 then
    	priceDiffClass = 'positive'
    	priceDiffSlide = 'slide-up'
    	priceDiffIcon = 'Up'
    elseif priceDiff < 0 then
    	priceDiffClass = 'negative'
    	priceDiffSlide = 'slide-down'
    	priceDiffIcon = 'Down'
    end
    
    if bulkData.last and bulkData.last > 0 then
    	priceDiffPerc = round(priceDiff / bulkData.last * 100, 2) .. '%'
    end
    if priceDiff >= 0 then
    	priceDiffF = '+'..priceDiffF
    	priceDiffPerc = '+'..priceDiffPerc
    end
    
    -- workaround so we can use `table.concat`
    usage = {}

    for k, v in ipairs( data.usage ) do
        usage[k] = v
    end

    if #usage == 0 then
    	usage = "\n* ''None''"
	else
        usage = '\n* [[' .. table.concat( usage, ']]\n* [[' ) .. ']]'
    end
    
    local noErr, chartdata = pcall( excgdata, { item }, true )
    
    if not noErr then
        chartdata = 'Chart not available'
    end

    -- build table
    local div = mw.html.create('div')
    	:addClass('gemw-container')
		
	if data.historical then
		div:addClass('gemw-historical')
        date = lang:formatDate( 'j F Y, H:i "(UTC)"', data.date )
        timeagodate = data.date
	end
	
    	--header
    div	:tag('div')
    		:addClass('gemw-header')
    		:addClass(priceDiffClass)
    		:tag('div')
    			:addClass('gemw-section-left')
                :tag('p')
                    :addClass('gemw-image inventory-image')
                    :wikitext('[[File:'..itemIcon..'|link='..data.item..']]')
                    :done()
    			:tag('p')
    				:addClass('gemw-name')
    				:wikitext('[['..data.item..']]')
    				:done()
    			:tag('p')
    				:addClass('gemw-examine')
    				:wikitext(data.examine or '')
    				:done()
    			:tag('div')
    				:tag('span')
    					:addClass('gemw-price')
    					:addClass(priceDiffSlide)
    					:attr('id', 'GEPrice')
    					:wikitext(price)
    					:done()
    				:tag('span')
    					:addClass('gemw-change')
    					:addClass(priceDiffSlide..'-2')
    					:wikitext(priceDiffF)
    					:wikitext(' ')
    					:wikitext(priceDiffPerc)
    					:wikitext(' [[File:'..priceDiffIcon..'.svg|12px|link=]]')
    					:done()
    				:done()
    			:done()
    		:tag('div')	
    			:addClass('gemw-section-right')
    			:tag('p')
    				:addClass('gemw-updated')
    				:attr('data-date', date) -- used by [[MediaWiki:Gadget-gemwupdate.js]]
    				:wikitext('Last updated ')
    				:tag('span')
    					:addClass('gemw-time')
    					:wikitext(timeago{timeagodate, purge='yes', purgeText='refresh'})
    					:done()
    				:tag('br'):done()
    				:wikitext(' on ')
    				:wikitext(date)
    				:done()
    			:tag('div')
    				:addClass('gemw-button-wrapper gemw-update-price')
    				:attr('id', 'gemw_guide')
    				:wikitext('')
    				:done()
    			:done()	
    		:done() --close header
    
    -- body		
    local dlTag = div	:tag('div')
    		:addClass('gemw-body')	
    		:tag('div')
    			:addClass('gemw-section-left')
    			:tag('dl')
    			
    dlTag			:tag('div')
    					:addClass('gemw-property gemw-members')
    					:tag('dt')
    						:wikitext('Status')
    						:done()
    					:tag('dd')
    						:wikitext(memsIcon)
    						:done()
    					:done()
    				:tag('div')
    					:addClass('gemw-property gemw-limit')
    					:tag('dt')
    						:wikitext('[[Grand Exchange#Trade restrictions|Buy limit]]')
    						:done()
    					:tag('dd')
    						:attr('id', 'exchange-limit')
    						:wikitext(data.limit and lang:formatNum( data.limit ) or '<i>Unknown</i>' )
    						:done()
    					:done()
    				:tag('div')
    					:addClass('gemw-property gemw-id')
    					:tag('dt')
    						:wikitext('Item ID')
    						:done()
    					:tag('dd')
    						:attr('id', 'exchange-itemid')
    						:wikitext(data.itemId or '<i>Unknown</i>')
    						:done()
    					:done()
    				:tag('div')
    					:addClass('gemw-property gemw-highalch')
    					:tag('dt')
    						:wikitext('[[High Level Alchemy|High Alchemy]]')
    						:done()
    					:tag('dd')
    						:attr('id', 'exchange-highalch')
    						:wikitext(highAlch)
    						:done()
    					:done()
    				:tag('div')
    					:addClass('gemw-property gemw-lowalch')
    					:tag('dt')
    						:wikitext('[[Low Level Alchemy|Low Alchemy]]')
    						:done()
    					:tag('dd')
    						:attr('id', 'exchange-lowalch')
    						:wikitext(lowAlch)
    						:done()
    					:done()
    				:tag('div')
    					:addClass('gemw-property gemw-value')
    					:tag('dt')
    						:wikitext('[[Value]]')
    						:done()
    					:tag('dd')
    						:attr('id', 'exchange-value')
    						:wikitext(data.value and lang:formatNum( data.value ) or '<i>Unknown</i>')
    						:done()
    					:done()
    				:done()
    			:tag('div')
    				:tag('p')
    					:addClass('gemw-links')
    					:wikitext('External links')
    					:done()
    				:tag('div')
    					:addClass('gemw-button secondary')
    					:wikitext('[http://services.runescape.com/m=itemdb_rs/viewitem.ws?obj=' .. data.itemId .. ' Official GE database]')
    					:done()
    				:tag('div')
    					:addClass('gemw-button secondary')
    					:wikitext('[http://services.runescape.com/m=itemdb_rs/results.ws?query=' .. mw.uri.encode( item, 'QUERY' ) .. ' Related items]')
    					:done()
    				:done()
    			:tag('div')
    				:tag('p')
    					:addClass('gemw-links')
    					:wikitext('Module links')
    					:done()
    				:tag('div')
    					:addClass('gemw-button secondary')
    					:wikitext('[[Module:Exchange/' .. item .. '|Exchange info]]')
    					:done()
    				:done()
    			:done()
    		:tag('div')
    			:addClass('gemw-section-right gemw-chart')
    			:wikitext(chartdata)
    			:done()

	if data.volume and ( dateDiffZ + 365 * dateDiffY ) <= 7 then
		dlTag:tag('div')
				:addClass('gemw-property gemw-volume')
				:tag('dt')
					:wikitext('7-day volume')
					:done()
				:tag('dd')
					:wikitext(lang:formatNum( 1000000 * data.volume ))
					:done()
	end

    div = tostring( div )

    -- categories
    local cats = ''
    local natPrice

    if title.nsText == 'Exchange' then
        cats = cats .. '[[Category:Grand Exchange]]'
        if data.historical then
        	cats = cats .. '[[Category:Historical Grand Exchange]]'
        else
            cats = cats .. '[[Category:Grand Exchange by date updated|*' .. lang:formatDate( 'c', bulkData.date ) .. data.itemId .. ']]'
        end

        if item ~= title.text then
            cats = cats .. '[[Category:Exchange names that needs checking]]'
        end

        -- have to preprocess this for it to work
        cats = cats .. frame:preprocess( '{{DEFAULTSORT:' .. data.item .. '}}' )

        if not data.historical then 
        	if bulkData.price and data.value and ( data.alchable == nil or yesno( data.alchable ) ) then
	            natPrice = exchange.loadBulkData('Nature rune', 'price')
	
	            if math.floor( ( data.value * 0.6 ) - bulkData.price - natPrice ) > 0 then
	                cats = cats .. '[[Category:Profitable Alchemy Items]]'
	            end
	
	            if math.floor( ( data.value * 0.4 ) - bulkData.price - natPrice ) > 0 then
	                cats = cats .. '[[Category:Profitable Low Alchemy Items]]'
	            end
	
	            if math.floor( ( data.value * 0.3 ) - bulkData.price ) > 0 then
	                cats = cats .. '[[Category:Profitable General Store Items]]'
	            end
	
	        elseif not data.value then
	            cats = cats .. '[[Category:Exchange items missing value]]'
	        end
	        
	        if data.volume then
	            cats = cats .. '[[Category:Exchange items with trade volume]]'
	        end
        end
        	

        -- this doesn't allow for the update time to be in the previous day but less than 24 hours
        -- if that's the case, then the page will end up in the 7 days category
        -- the same will happen for the other 2 categories too
        -- but it's not so noticeable given the already elapsed time
        local lastUpdate
        if not data.historical then
        	lastUpdate = ( lang:formatDate( 'z' ) - lang:formatDate( 'z', bulkData.date ) + 365 * ( lang:formatDate( 'Y' ) - lang:formatDate( 'Y', bulkData.date ) ) + 3 ) / 7
	        lastUpdate = round( lastUpdate, 0 )
	 
	        if lastUpdate == 1 then
	            cats = cats .. '[[Category:Needs price update/7 days]]'
	        elseif lastUpdate >= 2 and lastUpdate <= 4 then
	            cats = cats .. '[[Category:Needs price update/28 days]]'
	        elseif lastUpdate > 4 then
	            cats = cats .. '[[Category:Needs price update/29+ days]]'
	        end
	
	        if not data.limit then
	            cats = cats .. '[[Category:Exchange items missing limit]]'
	        end
	
	        if not data.category then
	            cats = cats .. '[[Category:Exchange items missing category]]'
	        end
	
	        -- Error checking
	        if not bulkData.price or bulkData.price < 1 then
	            cats = cats .. '[[Category:Exchange items with no price]]'
	        end
        end
    end
    
    local smw_to_json_names = {
    	id = 'Exchange ID',
    	name = 'Exchange name',
    	limit = 'Exchange limit',
    	value = 'Exchange value',
    	isalchable = 'Exchange is alchable',
    	highalch = 'Exchange high alch',
    	lowalch = 'Exchange low alch',
    	info = 'Exchange module',
    	history = 'Exchange history module',
    	historical = 'Exchange is historical'
    }
    local smw_json = {}
    local smw_data_arr = {}
    smw_json.id = data.itemId
    smw_json.name = item
    smw_json.limit = data.limit
    smw_json.value = data.value
    smw_json.isalchable = data.alchable == nil or yesno( data.alchable )
    if data.historical == true then
    	-- ensure data type correctness
    	smw_json.historical = true
    else
    	smw_json.historical = false
    end
    if smw_json.isalchable then
    	smw_json.highalch = rawHighAlch
    	smw_json.lowalch = rawLowAlch
    end
    smw_json.info = 'Module:Exchange/' .. data.item
    smw_json.history = 'Module:Exchange/' .. data.item .. '/Data'
    
    for k,v in pairs(smw_to_json_names) do
    	smw_data_arr[v] = smw_json[k]
    end
    local smwJsonGood, smwJsonEncoded = pcall(mw.text.jsonEncode,smw_json)
	if smwJsonGood then
		smw_data_arr['Exchange JSON'] = smwJsonEncoded
	else
		div = div .. '<div style="display:none;">Error making JSON for SMW[[Category:SMW errors]]</div>'
	end
    local res = mw.smw.set(smw_data_arr)
	if not res == true then
		div = div .. '<div style="display:none;">Error setting SMW properties: '..res.error..'[[Category:SMW errors]]</div>'
	end
 
    return div .. cats
end

function p.exchangeItem( frame )
	local fargs = frame.args
    local pargs = frame:getParent().args
    local item = pargs[1] or fargs.item	
    item = exchange.checkTitle( item )
    return p.main( item )
end

return p