Mooduul:ClimateFromCommons
Olgoldâshäämi
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