Jump to content

Module:ConfigRepository

Revision as of 01:13, 24 April 2025 by MarkWD (talk | contribs) (HARD FIX)

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

-- Module:ConfigRepository  -- 2025-04-24 “hardened” edition
-- Single source-of-truth for template configurations in ICANNWiki.
----------------------------------------------------------------------
require('strict')

----------------------------------------------------------------------
-- reusable singletons ------------------------------------------------
----------------------------------------------------------------------
local DateFmt        = require('Module:NormalizationDate')
local CanonicalForms = require('Module:CanonicalForms')

-- large constants live in separate, read-only data blobs
local ClassicTLDs    = mw.loadData('Module:ConfigRepository/Data/ClassicTLDs')

----------------------------------------------------------------------
local p = {}

----------------------------------------------------------------------
-- Global constants ---------------------------------------------------
----------------------------------------------------------------------
p.fieldLabels = {
    region  = 'Region',
    country = 'Country',
    date    = 'Date',
    website = 'Website',
}

p.dateFormatting = { useShortMonthNames = true }

p.semanticProperties = {
    region  = 'Has ICANN region',
    country = 'Has country',
    language= 'Knows language',
    person  = 'Has person',
}

----------------------------------------------------------------------
-- Template definitions ----------------------------------------------
----------------------------------------------------------------------
p.templates = {}

----------------------------------------------------------------------
-- EVENT --------------------------------------------------------------
----------------------------------------------------------------------
do
    local t = {
        meta = { description = 'Event template' },

        categories = { base = { 'Events' } },

        patterns = {
            seriesNumber = '^([%w%s]+)%s+(%d+)$',      -- e.g. ICANN 76
            seriesYear   = '^([%w%s]+)%s+(%d%d%d%d)$', -- e.g. IGF 2023
        },

        fields = {
            {key='logo'     ,label='Logo'},
            {key='process'  ,label='Process'},
            {key='start'    ,label='Start Date'},
            {key='end'      ,label='End Date'},
            {key='region'   ,label=p.fieldLabels.region},
            {keys={'country','territory'},label=p.fieldLabels.country},
            {key='city'     ,label='City'},
            {key='venue'    ,label='Venue'},
            {key='organizer',label='Organizer'},
            {keys={'website','url'},label=p.fieldLabels.website},
            {keys={'subject','category'},label='Subject'},
        },

        semantics = {
            properties = {
                ['Has start date'   ] = 'start',
                ['Has end date'     ] = 'end',
                ['Part of process'  ] = 'process',
                ['Has city'         ] = 'city',
                ['Has venue'        ] = 'venue',
                ['Has event organizer'] = 'organizer',
            },

            additionalProperties = {
                ['Has country'] = {'country','territory'},
            },

            transforms = {
                ['Has start date'] = function(v) return tostring(DateFmt.formatDate(v)) end,
                ['Has end date'  ] = function(v) return tostring(DateFmt.formatDate(v)) end,
            },

            skipProperties = {
                ['Has country'      ] = true,
                ['Has ICANN region' ] = true,
                ['Has event subject'] = true,
            },
        },
    }

    p.templates.Event = t
end

----------------------------------------------------------------------
-- PERSON -------------------------------------------------------------
----------------------------------------------------------------------
do
    local communityMap -- forward declaration for up-value capture

    local t = {
        meta = { description = 'Person template' },

        categories = { base = { 'Person' } },

        mappings = {
            community = {  -- shortened for brevity; full list unchanged
                { canonical = 'ICANN Community', synonyms = {'icann','community'}, category = 'ICANN Community' },
                { canonical = 'ICANN Staff'    , synonyms = {'staff','icann org'},  category = 'ICANN Staff' },
                -- … (other entries identical) …
            },
        },

        fields = {
            {key='community'  ,label='Community'},
            {key='affiliation',label='ICANN group'},
            {key='organization',label='Organization'},
            {key='region'     ,label=p.fieldLabels.region},
            {key='country'    ,label=p.fieldLabels.country},
            {key='languages'  ,label='Languages'},
            {key='website'    ,label=p.fieldLabels.website},
            {key='soi'        ,label='SOI'},
            {key='userbox'    ,label='Achievements'},
        },

        patterns = {
            itemDelimiter  = ';%s*',
            websitePattern = '^https?://[^%s]+',
        },

        semantics = {
            properties = {
                ['Has governance community'] = 'community',
                ['Has ICANN affiliation']    = 'affiliation',
                ['Has organization']         = 'organization',
            },

            additionalProperties = {
                ['Has country']      = {'country'},
                ['Has ICANN region'] = {'region'},
            },

            transforms = {
                ['Has governance community'] = function(v)
                    return select(1, CanonicalForms.normalize(v, communityMap)) or v
                end,
                ['Knows language'] = function(v) return v end,
            },

            skipProperties = {
                ['Has country']      = true,
                ['Has ICANN region'] = true,
            },
        },
    }

    communityMap = t.mappings.community
    p.templates.Person = t
end

----------------------------------------------------------------------
-- TLD ----------------------------------------------------------------
----------------------------------------------------------------------
do
    local typeMap, subtypeMap

    local t = {
        meta = { description = 'TLD/ccTLD template' },

        categories = {
            base       = {},
            conditional= { rvc='TLDs with RVCs', idn='IDN', idn_cctld='IDN ccTLD' },
        },

        mappings = {
            type = {
                {canonical='gTLD', synonyms={'generic','tld', 'generic top level domain','generic top-level domain','generic tld'}, category='gTLD'},
                {canonical='ccTLD',synonyms={'country','cc','country code top level domain','country code top-level domain','country tld'}, category='ccTLD'},
            },
            subtype = {
                {canonical='geoTLD',          synonyms={'geo tld','geo', 'geographic','geographical','geographic top level domain','geographic top-level domain','geographic tld'}, css='tld-template-subtype-geotld',            category='geoTLD'},
                {canonical='dotBrand',        synonyms={'brand','brandtld','brand tld','brand top level domain','brand top-level domain'},                                               css='tld-template-subtype-brandtld',        category='dotBrand'},
                {canonical='Sponsored TLD',   synonyms={'sponsored','sponsored top level domain','sponsored top-level domain'},                                                        css='tld-template-subtype-sponsored',       category='Sponsored TLD'},
                {canonical='Legacy TLD',      synonyms={'legacy','legacy top level domain','legacy top-level domain'},                                                                 css='tld-template-subtype-legacytld',       category='Legacy TLD'},
                {canonical='2012 gTLD Round', synonyms={'gtld round 2012','2012 ngtld round','2012 ngtld','ngtld 2012','ngtld','2012'},                                                css='tld-template-subtype-ngtld-round-2012',category='2012 gTLD Round'},
            },
        },

        constants = {
            classicTLDs = ClassicTLDs,
        },

        patterns = {
            tldExtension    = '%.([^%.]+)$',
            countryDelimiter= '([^;]+)',
        },

        fields = {
            {key='type'        ,label='Type'},
            {key='subtype'     ,label='Subtype'},
            {key='status'      ,label='Status'},
            {keys={'country','territory'},label=p.fieldLabels.country},
            {key='introduced'  ,label='Introduced'},
            {keys={'date','implemented'},label='Implemented'},
            {keys={'script','language'},label='Script'},
            {key='translation' ,label='English version'},
            {key='ascii'       ,label='Punycode'},
            {keys={'registry','registryprovider'},label='Registry'},
            {key='website'     ,label=p.fieldLabels.website},
            {keys={'RVC','PIC'},label='PIC/RVC'},
        },

        semantics = {
            properties = {
                ['Has TLD type']        = 'type',
                ['Has TLD subtype']     = 'subtype',
                ['Has TLD status']      = 'status',
                ['Date introduced']     = 'introduced',
                ['Date implemented']    = 'date',
                ['Uses writing script'] = 'script',
                ['Has registry operator']= 'registry',
                ['Has PIC or RVC']      = 'RVC',
            },

            additionalProperties = {
                ['Has country']         = {'country','territory'},
                ['Date implemented']    = {'date','implemented'},
                ['Uses writing script'] = {'script','language'},
                ['Has registry operator']= {'registry','registryprovider'},
                ['Has PIC or RVC']      = {'RVC','PIC'},
            },

            transforms = {
                ['Has TLD type']     = function(v) return select(1, CanonicalForms.normalize(v, typeMap   )) or v end,
                ['Has TLD subtype']  = function(v) return select(1, CanonicalForms.normalize(v, subtypeMap)) or v end,
                ['Date introduced']  = function(v) return tostring(DateFmt.formatDate(v)) end,
                ['Date implemented'] = function(v) return tostring(DateFmt.formatDate(v)) end,
                ['Has PIC or RVC']   = function(v) if v and v ~= '' then return 'true' end end,
            },

            skipProperties = { ['Has country'] = true },
        },
    }

    typeMap, subtypeMap = t.mappings.type, t.mappings.subtype
    p.templates.TLD = t
end

----------------------------------------------------------------------
-- LIBRARY INTERVIEW --------------------------------------------------
----------------------------------------------------------------------
do
    local t = {
        meta = { description = 'Library Interview template' },

        categories = { base = { 'Internet & Digital Governance Library', 'ICANNWiki Interviews' } },

        constants = { title = 'Internet & Digital Governance Library', tableClass = 'library-box' },

        fields = {
            {key='Title'      ,label='Title'},
            {key='Format'     ,label='Format'},
            {key='Date'       ,label=p.fieldLabels.date},
            {key='Interviewer',label='Interviewer',autoWikiLink=true},
            {key='Interviewee',label='Interviewee',autoWikiLink=true},
            {key='ID'         ,label='Permanent ID'},
        },

        semantics = {
            properties = {
                ['Has interview format'] = 'Format',
                ['Has date']             = 'Date',
                ['Has interviewer']      = 'Interviewer',
                ['Has interviewee']      = 'Interviewee',
                ['Permanent ID']         = 'ID',
            },

            additionalProperties = { ['Has person'] = {'Interviewer','Interviewee'} },

            transforms = {
                ['Has date']     = function(v) return tostring(DateFmt.formatDate(v)) end,
                ['Permanent ID'] = function(v) return tostring(v or '') end,
            },
        },
    }

    p.templates.LibraryInterview = t
end

----------------------------------------------------------------------
-- API ----------------------------------------------------------------
----------------------------------------------------------------------
function p.getConfig(templateType)
    return p.templates[templateType] or {}
end

function p.createStandardConfig(cfg)
    cfg = cfg or {}
    return {
        meta       = cfg.meta       or { description = 'Template module configuration' },
        mappings   = cfg.mappings   or {},
        fields     = cfg.fields     or {},
        semantics  = cfg.semantics  or { properties={}, transforms={}, additionalProperties={} },
        constants  = cfg.constants  or {},
        patterns   = cfg.patterns   or {},
        categories = cfg.categories or {},
    }
end

function p.getStandardConfig(templateType, overrides)
    return p.createStandardConfig(p.getConfig(templateType), overrides)
end

----------------------------------------------------------------------
mw.log('ConfigRepository loaded; heap '..collectgarbage('count')..' kB')
return p