Njuškii siskáldâsân

Mooduul:ClimateFromCommons

Wikipedia:st

Taan mooduul ravvuu puáhtá rähtiđ siijđon Mooduul:ClimateFromCommons/raavâ

-- Module:ClimateFromCommons

local p = {}

-- ========== helpers ==========

local function avg(t)
	if not t or #t == 0 then return nil end
	local s = 0
	for i = 1, #t do s = s + t[i] end
	return s / #t
end

-- always pass a NUMBER to formatNum
local function roundStr(n, decimals, lang)
	if type(n) ~= 'number' then return '—' end
	decimals = tonumber(decimals) or 1
	local s = mw.ustring.format('%.' .. decimals .. 'f', n)
	local num = tonumber(s)
	return (lang or mw.getContentLanguage()):formatNum(num)
end

local function toUnit(val, unit)
	if type(val) ~= 'number' then return val end
	if unit == '°F' or unit == 'F' then
		return (val * 9/5) + 32
	end
	return val
end

local function getLang(code)
	return code and mw.language.new(code) or mw.getContentLanguage()
end

local function monthName(lang, m)
	return lang:formatDate('F', string.format('2000-%02d-01', m))
end

local function inferPlaceFromDataset(dataset)
	local base = (dataset or ''):gsub('^Data:', '')
	base = base:gsub('.-/', ''):gsub('%.tab$', ''):gsub('_',' ')
	return base ~= '' and base or 'Location'
end

-- robust fetch
local function getTabular(page)
	if not page or page == '' then return nil, 'No dataset specified.' end
	local name = page:gsub('^Data:', '')
	local tries = {
		function() return mw.ext.data.get(name) end,
		function() return mw.ext.data.get('Data:' .. name) end,
		function() return mw.ext.data.get(name, '_') end,
		function() return mw.ext.data.get('Data:' .. name, '_') end,
	}
	for _, f in ipairs(tries) do
		local ok, data = pcall(f)
		if ok and data and data.data and (data.schema and data.schema.fields) then
			return data
		end
	end
	return nil, 'Could not load tabular data from "' .. name .. '"'
end

-- ========== core ==========

function p._build(args)
	local rawDataset = args.dataset or 'Ncei.noaa.gov/weather/New_York_City.tab'
	local tab, err = getTabular(rawDataset)
	if not tab then
		return string.format("'''Error:''' %s", mw.text.nowiki(err or ('Could not load data from "' .. rawDataset .. '"')))
	end

	-- field index
	local idx = {}
	for i, f in ipairs(tab.schema.fields or {}) do
		if f and f.name then idx[f.name] = i end
	end
	for _, need in ipairs({'date','avgHighTemp','avgLowTemp','highTemp','lowTemp'}) do
		if not idx[need] then
			return string.format("'''Error:''' Dataset \"%s\" is missing \"%s\".", rawDataset, need)
		end
	end

	-- accumulate by calendar month
	local byM = {}
	for m = 1, 12 do
		byM[m] = { avgHigh = {}, avgLow = {}, recHigh = -1/0, recLow = 1/0 }
	end
	local overallHigh, overallLow = -1/0, 1/0

	for _, row in ipairs(tab.data) do
		local d = row[idx.date]
		local _, m = tostring(d):match('^(%d+)%-(%d+)$')
		local mi = tonumber(m)
		if mi and mi >= 1 and mi <= 12 then
			local ah = tonumber(row[idx.avgHighTemp])
			local al = tonumber(row[idx.avgLowTemp])
			local hi = tonumber(row[idx.highTemp])
			local lo = tonumber(row[idx.lowTemp])

			if ah then table.insert(byM[mi].avgHigh, ah) end
			if al then table.insert(byM[mi].avgLow,  al) end
			if hi and hi > byM[mi].recHigh then byM[mi].recHigh = hi end
			if lo and lo < byM[mi].recLow  then byM[mi].recLow  = lo end
			if hi and hi > overallHigh then overallHigh = hi end
			if lo and lo < overallLow  then overallLow  = lo end
		end
	end

	-- compute monthly stats
	local monthly = {}
	for m = 1, 12 do
		local mh = avg(byM[m].avgHigh)
		local ml = avg(byM[m].avgLow)
		local mm = (mh and ml) and (mh + ml) / 2 or nil
		if byM[m].recHigh == -1/0 then byM[m].recHigh = nil end
		if byM[m].recLow  ==  1/0 then byM[m].recLow  = nil end
		monthly[m] = { avgHigh = mh, avgLow = ml, mean = mm, recHigh = byM[m].recHigh, recLow = byM[m].recLow }
	end

	-- annual means
	local function meanOf(field)
		local t = {}
		for m = 1, 12 do
			local v = monthly[m][field]
			if type(v) == 'number' then t[#t+1] = v end
		end
		return avg(t)
	end
	local yAvgHigh = meanOf('avgHigh')
	local yAvgLow  = meanOf('avgLow')
	local yMean    = meanOf('mean')

	-- formatting
	local lang   = getLang(args.lang or 'smn')
	local unit   = (args.unit == 'F' or args.unit == '°F') and '°F' or '°C'
	local dps    = tonumber(args.decimals) or 1

	-- Inari Sámi label defaults (overrideable)
	local L_month = args.col_month or 'Mánuppaje'
	local L_ah    = args.col_ah    or 'KL (max)'
	local L_al    = args.col_al    or 'KL (min)'
	local L_mean  = args.col_mean  or 'KL (peivi)'
	local L_rh    = args.col_rh    or 'Ulâttâs (max)'
	local L_rl    = args.col_rl    or 'Ulâttâs (min)'
	local L_year  = args.row_year  or 'Ihe'

	local out = {}
	-- margin-bottom:0 to avoid any extra space under the table
	table.insert(out, '{| class="wikitable" style="text-align:center; width:100%; max-width:720px; margin-bottom:0"')
	-- Optional caption only if provided
	if args.caption and args.caption ~= '' then
		table.insert(out, '|+ ' .. args.caption)
	end
	table.insert(out, '! ' .. table.concat({L_month,L_ah,L_al,L_mean,L_rh,L_rl}, ' !! '))

	for m = 1, 12 do
		local r = monthly[m]
		table.insert(out, '|-')
		table.insert(out, string.format(
			'| %s || %s || %s || %s || %s || %s',
			monthName(lang, m),
			roundStr(toUnit(r.avgHigh, unit), dps, lang),
			roundStr(toUnit(r.avgLow,  unit), dps, lang),
			roundStr(toUnit(r.mean,    unit), dps, lang),
			roundStr(toUnit(r.recHigh, unit), dps, lang),
			roundStr(toUnit(r.recLow,  unit), dps, lang)
		))
	end

	table.insert(out, '|- style="font-weight:bold;background:#f7f7f7"')
	table.insert(out, string.format(
		'| %s || %s || %s || %s || %s || %s',
		L_year,
		roundStr(toUnit(yAvgHigh,   unit), dps, lang),
		roundStr(toUnit(yAvgLow,    unit), dps, lang),
		roundStr(toUnit(yMean,      unit), dps, lang),
		roundStr(toUnit(overallHigh,unit), dps, lang),
		roundStr(toUnit(overallLow, unit), dps, lang)
	))

	-- source row INSIDE the table
	if args.source ~= 'no' then
		table.insert(out, '|-')
		table.insert(out, string.format(
			'| colspan="6" style="text-align:center;font-size:100%%;padding:0.35em 0.6em;" | %s: [[Commons:Data:%s|Wikimedia Commons]]',
			args.source_label or 'Käldee', rawDataset
		))
	end

	table.insert(out, '|}')
	-- Trim to avoid stray whitespace creating a blank paragraph
	return mw.text.trim(table.concat(out, '\n'))
end

function p.table(frame)
	local args = frame:getParent() and frame:getParent().args or frame.args
	return p._build(args)
end

return p