Moduł:Parser wikiprojektów

Z Wikipedii, wolnej encyklopedii
 Dokumentacja modułu [zobacz] [edytuj] [historia] [odśwież]

Moduł służy do wyświetlania spisu wikiprojektów w sposób czytelny dla osób nietechnicznych. Wczytuje listę w formacie JSON ze strony Wikipedia:Wikiprojekt/Spis wikiprojektów.json.

Moduł eksportuje jedną bezargumentową funkcję: Lista, odpowiedzialną za wczytanie, przetworzenie i wyświetlenie listy. Sposób wywołania:

{{#invoke:Parser wikiprojektów|Lista}}
-- Definiuje kolejność sekcji z dziedzinami
-- oraz dopuszczalne dziedziny
-- Nierozpoznane jest na pierwszym miejscu, aby zwracać uwagę
local projectCategoryUnknown = 'Nierozpoznane'
local standardProjectCategories = {
    projectCategoryUnknown,
    'Nauki ścisłe i przyrodnicze',
    'Nauki społeczne i humanistyczne',
    'Społeczeństwo',
    'Geografia',
    'Polska',
    'Kultura',
    'Sport',
    'Technika'
}

-- Lua sortuje stringi jakby to były ciągi bajtów,
-- więc trzeba zdefiniować klucz sortowania składający się tylko z ASCII
function createSortKey(name)
    local replacements = {
        ['ą'] = 'azz',
        ['ć'] = 'czz',
        ['ę'] = 'ezz',
        ['ł'] = 'lzz',
        ['ń'] = 'nzz',
        ['ó'] = 'ozz',
        ['ś'] = 'szz',
        ['ź'] = 'zzy',
        ['ż'] = 'zzz',
        ['Ą'] = 'AZZ',
        ['Ć'] = 'CZZ',
        ['Ę'] = 'EZZ',
        ['Ł'] = 'LZZ',
        ['Ń'] = 'NZZ',
        ['Ó'] = 'OZZ',
        ['Ś'] = 'SZZ',
        ['Ź'] = 'ZZY',
        ['Ż'] = 'ZZZ'
    }

    name = mw.ustring.gsub(name, '%a', replacements)
    return name
end

-- Uzupełnia opcjonalne pola
function normalizeWikiproject(wikiproject)
    wikiproject['name_sortkey'] = createSortKey(wikiproject['name'])
    if wikiproject['page'] == nil then
        wikiproject['page'] = 'Wikiprojekt:' .. wikiproject['name']
    end
    if wikiproject['talk'] == nil then
        wikiproject['talk'] = 'Dyskusja wikiprojektu:' .. wikiproject['name']
    end
    if wikiproject['report_link'] == nil then
        wikiproject['report_link'] = 'Specjalna:Nowa_sekcja/' .. wikiproject['talk']
    end
    if wikiproject['join'] ~= nil then
        wikiproject['join'] = string.gsub(wikiproject['join'], ' ', '_')
    end
    if wikiproject['portal_page'] == nil and wikiproject['portal_name'] ~= nil then
        if type(wikiproject['portal_name']) == 'string' then
            wikiproject['portal_page'] = 'Portal:' .. wikiproject['portal_name']
        else
            wikiproject['portal_page'] = {}
            for i, portal_name in ipairs(wikiproject['portal_name']) do
                table.insert(wikiproject['portal_page'], 'Portal:' .. portal_name)
            end
        end
    end
    if type(wikiproject['portal_name']) == 'string' then
        wikiproject['portal_name'] = { wikiproject['portal_name'] }
        wikiproject['portal_page'] = { wikiproject['portal_page'] }
    end
    if wikiproject['portal_name'] == nil then
        wikiproject['portal_name'] = {}
        wikiproject['portal_page'] = {}
    end

    if wikiproject['category_leader'] == nil then
        wikiproject['category_leader'] = false
    end

    if wikiproject['children'] == nil then
        wikiproject['children'] = {}
    end
    for i, child in ipairs(wikiproject['children']) do
        normalizeWikiproject(child)
    end
end

-- Dzieli projekty na aktywne i nieaktywne
function groupByActivity(wikiprojects)
    local active = {}
    local inactive = {}
    for i, wikiproject in ipairs(wikiprojects) do
        if wikiproject['active'] then
            table.insert(active, wikiproject)
        else
            table.insert(inactive, wikiproject)
        end
    end
    return active, inactive
end

-- Grupuje projekty według typów
function groupByType(wikiprojects)
    local result = {}
    for i, wikiproject in ipairs(wikiprojects) do
        if result[wikiproject['type']] == nil then
            result[wikiproject['type']] = {}
        end
        table.insert(result[wikiproject['type']], wikiproject)
    end
    return result
end

-- Grupuje projekty według dziedziny wiedzy (kategorii)
function groupByCategory(wikiprojects)
    local grouped = {}
    local noGroup = projectCategoryUnknown

    for i, wikiproject in ipairs(wikiprojects) do
        -- Sprawdź, czy wikiprojekt jest w zdefiniowanej kategorii
        local isKnownCategory = false
        for j, category in ipairs(standardProjectCategories) do
            if wikiproject['category'] == category then
                isKnownCategory = true
                break
            end
        end
        if not isKnownCategory then
            wikiproject['category'] = nil
        end

        -- Przypisz do podtabeli odpowiedniej dla kategorii
        if wikiproject['category'] ~= nil then
            if grouped[wikiproject['category']] == nil then
                grouped[wikiproject['category']] = {}
            end
            table.insert(grouped[wikiproject['category']], wikiproject)
        else
            if grouped[noGroup] == nil then
                grouped[noGroup] = {}
            end
            table.insert(grouped[noGroup], wikiproject)
        end
    end

    return grouped
end

function sortWikiprojects(wikiprojects)
    table.sort(wikiprojects, function(a, b)
        if a['category_leader'] ~= b['category_leader'] then
            if a['category_leader'] then
                return true
            else
                return false
            end
        end

        if a['active'] ~= b['active'] then
            -- Nieaktywny jest oznaczany jako false,
            -- Aktywne mogą mieć stringa lub true
            if not b['active'] then
                return true
            end
            if not a['active'] then
                return false
            end
        end

        return a['name_sortkey'] < b['name_sortkey']
    end)

    for i, wikiproject in ipairs(wikiprojects) do
        if wikiproject['children'] ~= nil then
            sortWikiprojects(wikiproject['children'])
        end
    end
end

-- Tworzy tabelę dla wikiprojektów
function buildTable(wikiprojects, isActive, headerFunction, rowFunction, width)
    sortWikiprojects(wikiprojects)

    local tbl = mw.html.create('table')
    tbl:addClass('wikitable')
        :addClass('sortable')
        :css('width', width)
    
    if not isActive then
        tbl:addClass('mw-collapsible')
            :addClass('mw-collapsed')
    end

    tbl:node(headerFunction(isActive))

    for i, wikiproject in pairs(wikiprojects) do
        local rows = rowFunction(wikiproject, 0, isActive)
        for i, row in pairs(rows) do
            tbl:node(row)
        end
    end

    return tbl
end

-- =============================================================
--              OBSŁUGA ZWYKŁYCH WIKIPROJEKTÓW
-- =============================================================

-- Tworzy nagłówek tabeli dla zwykłych wikiprojektów
function buildTableHeaderForStandard(isActive)
    local header = mw.html.create('tr')

    header:node(mw.html.create('th'):attr('width', '25%')
        :wikitext('Projekt (strona główna)'))
    if isActive then
        header:node(mw.html.create('th'):addClass('unsortable'):attr('width', '20%')
            :wikitext('Koordynacja działań (dyskusja)'))
        header:node(mw.html.create('th'):addClass('unsortable'):attr('width', '20%')
            :wikitext('Uczestnicy'))
    end
    header:node(mw.html.create('th'):attr('width', '10%')
        :wikitext('Data założenia'))
    header:node(mw.html.create('th'):addClass('unsortable'):attr('width', '25%')
        :wikitext('Powiązane portale'))

    return header
end

-- Tworzy wiersz tabeli dla zwykłych wikiprojektów oraz wiersze dla projektów "potomnych"
function buildTableRowsForStandard(wikiproject, childLevel, isActive)
    local tr = mw.html.create('tr')

    local paddingLeft = nil
    if childLevel > 0 then
        paddingLeft = (1.5 * childLevel) .. 'em'
    end

    if wikiproject['users'] == nil then
        wikiproject['users'] = '|<b style="color:red;">Brakuje strony: </b>'
    end

    tr:node(mw.html.create('th'):wikitext(
        '[[' .. wikiproject['page'] .. '|' .. wikiproject['name'] .. ']]'
    ):attr('scope', 'row'):css('padding-left', paddingLeft):css('text-align', 'left'))
    if isActive then
        tr:node(mw.html.create('td'):wikitext(
            '[[' .. wikiproject['talk'] .. '|(Dyskusja)]] → [[' .. wikiproject['report_link'] .. '|zgłoś problem]]'
        ))
        if wikiproject['join'] ~= nil then
            tr:node(mw.html.create('td'):wikitext(
                '[[' .. wikiproject['users'] .. '|Uczestnicy]] → [' .. wikiproject['join'] .. ' zapisz się]'
            ):addClass('plainlinks'))
        else
            tr:node(mw.html.create('td'):wikitext(
                '[[' .. wikiproject['users'] .. '|Uczestnicy → zapisz się]]'
            ))
        end
    end
    tr:node(mw.html.create('td'):wikitext(
        wikiproject['started']
    ))

    local portalsWikitext = ''
    if #wikiproject['portal_page'] == 1 then
        portalsWikitext = portalsWikitext .. '[[' .. wikiproject['portal_page'][1] .. '|' .. wikiproject['portal_name'][1] .. ']]'
    else
        for i = 1, #wikiproject['portal_page'] do
            if i > 1 then
                portalsWikitext = portalsWikitext .. '<br/>'
            end
            portalsWikitext = portalsWikitext .. '[[' .. wikiproject['portal_page'][i] .. '|' .. wikiproject['portal_name'][i] .. ']]'
        end
    end
    tr:node(mw.html.create('td'):wikitext(
        portalsWikitext
    ))

    local rowsList = { tr }
    if #wikiproject['children'] > 0 then
        for i, child in pairs(wikiproject['children']) do
            childRows = buildTableRowsForStandard(child, childLevel + 1, isActive)
            for i, row in pairs(childRows) do
                table.insert(rowsList, row)
            end
        end
    end

    return rowsList
end

-- =============================================================
--            OBSŁUGA WIKIPROJEKTÓW SPECJALNYCH
-- =============================================================

-- Tworzy nagłówek tabeli dla wikiprojektów specjalnych
function buildTableHeaderForSpecial(isActive)
    local header = mw.html.create('tr')

    header:node(mw.html.create('th'):wikitext('Projekt (strona główna)'))
    if isActive then
        header:node(mw.html.create('th'):wikitext('Koordynacja działań (dyskusja)'):addClass('unsortable'))
    end
    header:node(mw.html.create('th'):wikitext('Data założenia'))

    return header
end

-- Tworzy wiersz tabeli dla wikiprojektów specjalnych oraz wiersze dla ew. projektów "potomnych"
function buildTableRowsForSpecial(wikiproject, childLevel, isActive)
    local tr = mw.html.create('tr')

    local paddingLeft = nil
    if childLevel > 0 then
        paddingLeft = (1.5 * childLevel) .. 'em'
    end

    tr:node(mw.html.create('th'):wikitext(
        '[[' .. wikiproject['page'] .. '|' .. wikiproject['name'] .. ']]'
    ):attr('scope', 'row'):css('padding-left', paddingLeft):css('text-align', 'left'))
    if isActive then
        tr:node(mw.html.create('td'):wikitext(
            '[[' .. wikiproject['talk'] .. '|(Dyskusja)]] → [[' .. wikiproject['report_link'] .. '|zgłoś problem]]'
        ))
    end
    tr:node(mw.html.create('td'):wikitext(
        wikiproject['started']
    ))

    local rowsList = { tr }
    if #wikiproject['children'] > 0 then
        for i, child in pairs(wikiproject['children']) do
            childRows = buildTableRowsForSpecial(child, childLevel + 1, isActive)
            for i, row in pairs(childRows) do
                table.insert(rowsList, row)
            end
        end
    end

    return rowsList
end

-- =============================================================
--         OBSŁUGA WIKIPROJEKTÓW INSTYTUCJONALNYCH
-- =============================================================

-- Tworzy nagłówek tabeli dla wikiprojektów instytucjonalnych
function buildTableHeaderForInstitutional(isActive)
    local header = mw.html.create('tr')

    header:node(mw.html.create('th'):wikitext('Projekt (strona główna)'))
    header:node(mw.html.create('th'):wikitext('Uczestnicy projektu'):addClass('unsortable'))
    header:node(mw.html.create('th'):wikitext('Status'))

    return header
end

-- Tworzy wiersz tabeli dla wikiprojektów specjalnych oraz wiersze dla ew. projektów "potomnych"
function buildTableRowsForInstitutional(wikiproject, childLevel, isActive)
    local tr = mw.html.create('tr')

    local paddingLeft = nil
    if childLevel > 0 then
        paddingLeft = (1.5 * childLevel) .. 'em'
    end

    if type(wikiproject['active']) == 'boolean' then
        if wikiproject['active'] then
            wikiproject['active'] = 'trwający'
        else
            wikiproject['active'] = 'zakończony'
        end
    end

    tr:node(mw.html.create('th'):wikitext(
        '[[' .. wikiproject['page'] .. '|' .. wikiproject['name'] .. ']]'
    ):attr('scope', 'row'):css('padding-left', paddingLeft):css('text-align', 'left'))

    if wikiproject['users'] ~= nil then
        if wikiproject['join'] ~= nil then
            tr:node(mw.html.create('td'):wikitext(
                '[[' .. wikiproject['users'] .. '|Uczestnicy]] → [' .. wikiproject['join'] .. ' zapisz się]'
            ):addClass('plainlinks'))
        else
            tr:node(mw.html.create('td'):wikitext(
                '[[' .. wikiproject['users'] .. '|Uczestnicy → zapisz się]]'
            ))
        end
    else
        tr:node(mw.html.create('td'))
    end

    tr:node(mw.html.create('td'):wikitext(
        wikiproject['active']
    ))

    local rowsList = { tr }
    if #wikiproject['children'] > 0 then
        for i, child in pairs(wikiproject['children']) do
            childRows = buildTableRowsForInstitutional(child, childLevel + 1, isActive)
            for i, row in pairs(childRows) do
                table.insert(rowsList, row)
            end
        end
    end

    return rowsList
end



local p = {}

function p.Lista(frame)
    local jsonPage = mw.title.new('Wikipedia:Wikiprojekt/Spis_wikiprojektów.json')
    local wikiprojects = mw.text.jsonDecode(jsonPage:getContent())

    for i, wikiproject in pairs(wikiprojects) do
        normalizeWikiproject(wikiproject)
    end

    local output = {}
    local activeProjects, inactiveProjects = groupByActivity(wikiprojects)
    activeByType = groupByType(activeProjects)
    standardByCategory = groupByCategory(activeByType['normal'])

    table.insert(output, '== Aktywne wikiprojekty ==')
    table.insert(output, '=== Według dziedzin wiedzy ===')
    for i, category in ipairs(standardProjectCategories) do
        projects = standardByCategory[category]
        if projects ~= nil then
            table.insert(output, '==== ' .. category .. ' ====')
            table.insert(output, tostring(buildTable(
                projects, true, buildTableHeaderForStandard, buildTableRowsForStandard, '100%'
            )))
        end
    end

    table.insert(output, '=== Wikiprojekty specjalne ===')
    table.insert(output, 'Są to szczególne wikiprojekty, które od \'\'zwykłych\'\' odróżniają się tym, że nie są nastawione na merytoryczny rozwój [[Pomoc:Przestrzeń nazw#(główna)|przestrzeni głównej]].<br/>W większości są to \'\'metawikiprojekty\'\': koordynują działanie innych wikiprojektów lub zajmują się [[Pomoc:Słowniczek#M|metastronami]] Wikipedii (kategoriami, szablonami, plikami, zasadami Wikipedii, społecznością Wikipedii itd.).')
    table.insert(output, tostring(buildTable(
        activeByType['special'], true, buildTableHeaderForSpecial, buildTableRowsForSpecial, nil
    )))

    table.insert(output, '=== Wikiprojekty instytucjonalne ===')
    table.insert(output, 'Tutaj znajdują się wikiprojekty, które współtworzone są formalnie przez zewnętrzne instytucje, przy wsparciu Stowarzyszenia Wikimedia Polska.')
    table.insert(output, frame:expandTemplate{ title = 'Zobacz też', args = {'Wikipedia:Projekty szkolne i akademickie'}})
    table.insert(output, tostring(buildTable(
        activeByType['institutional'], true, buildTableHeaderForInstitutional, buildTableRowsForInstitutional, '100%'
    )))

    local inactiveByType = groupByType(inactiveProjects)
    table.insert(output, '== Nieaktywne wikiprojekty ==')
    table.insert(output, 'Poniższe wikiprojekty nie funkcjonują, a ich listy uczestników mogą być od dawna nieaktualne. Jeśli chcesz reaktywować któryś z nich, znajdź najpierw zespół wikipedystów, który będzie współpracował.\n\n; Jak to zrobić?\n* sprawdź, którzy z zapisanych uczestników są aktywni i czy nadal zajmują się przedmiotem zainteresowania wikiprojektu,\n* zaproś wikipedystów na nowych członków zespołu,\n* skreśl nieaktywnych wikipedystów i zarchiwizuj stare dyskusje,\n* o ile nowa grupa podejmie współpracę, wikiprojekt zyska nowe życie.')

    table.insert(output, '=== Nieaktywne wikiprojekty zwykłe ===')
    table.insert(output, tostring(buildTable(
        inactiveByType['normal'], false, buildTableHeaderForStandard, buildTableRowsForStandard, '100%'
    )))

    table.insert(output, '=== Nieaktywne wikiprojekty specjalne ===')
    table.insert(output, 'W odróżnieniu od \'\'zwykłych\'\' nieaktywnych wikiprojektów, które można wskrzesić, niektóre z poniższych są historyczne i ich aktywność nie jest już możliwa (np. [[Wikiprojekt:Sortowanie stubów|Wikiprojekt Sortowanie stubów]]).')
    table.insert(output, tostring(buildTable(
        inactiveByType['special'], false, buildTableHeaderForSpecial, buildTableRowsForSpecial, nil
    )))


    return table.concat(output, '\n')
end

return p