Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

// request.cf · coarse context

A page that knows where it met you.

Only coarse request metadata is shown. This demo does not display or persist visitor IP addresses.

Country
US
Cloudflare location
CMH
Connection
HTTP/2
Language
Not provided

Ray ID: a248675b3eab4c41

Jump to content

Module:Sandbox/Blippy1998/Legislature

From Wikipedia, the free encyclopedia
local p = {}

local seats
local members
local committees

local base_module_name = "Module:Sandbox/Blippy1998/Legislature"


-- ============================================================================
-- HELPERS
-- ============================================================================
function p.initEnvironment(args)
    local path_parts = {base_module_name}
    
    -- Iterate through all positional arguments (args[1], args[2], etc.)
    -- e.g., "US", "TX", "lower"
    local i = 1
    while args[i] do
        table.insert(path_parts, args[i])
        i = i + 1
    end
    
    -- Joins them into "Module:Legislature/US/TX/lower"
    local base_path = table.concat(path_parts, "/")
    
    -- Load the data modules dynamically
	seats = mw.loadData(base_path .. "/seats")
	local getSeats = require(base_module_name .. "/" .. args[1]).flattenDistricts
	if getSeats ~= nil then
		seats = getSeats(seats)
	end
    members = mw.loadData(base_path .. "/members")
    committees = mw.loadData(base_path .. "/committees")
    
    return args[1] -- Returns the top-level country code (e.g., "US", "BR")
end

-- Helper to safely extract arguments from a template frame or direct table call
function p.getArgs(frame)
    if type(frame) == 'table' and type(frame.args) == 'table' then
        return frame.args
    elseif type(frame) == 'table' then
        return frame
    else
        return { [1] = frame } -- Wraps a plain string into an args-like table
    end
end

function p.qidToWikilink(qid)
    -- Gets the actual English Wikipedia article title (e.g., "Jim Jordan (American politician)")
    local article = mw.wikibase.sitelink(qid)
    
    -- Gets the clean Wikidata label (e.g., "Jim Jordan")
    local label = mw.wikibase.label(qid)
    
    -- if article then
	if label and label ~= article then
		-- Returns: [[Jim Jordan (American politician)|Jim Jordan]]
		return "[[" .. article .. "|" .. label .. "]]"
	else
		-- Returns: [[Jim Jordan]]
		return "[[" .. article .. "]]"
	end
    -- else
    --     -- Fallback if they don't have an English Wikipedia article yet
    --     return label or qid 
    -- end
end

function p.nameToID(targetName, fieldName, dictionaryToSearch)
    targetName = targetName:lower()
    local matches = {}
	for id, data in pairs(dictionaryToSearch) do
        if data[fieldName]:lower():find(targetName, 1, true) then
            table.insert(matches, id)
        end
    end
	
	if #matches > 1 then
		error("name matched more than one id")
	end
	if #matches == 0 then
		return nil
	end
	return matches[1]
end

function p.repNameToID(targetName)
	return p.nameToID(targetName, "rep_name", members)
end

function p.committeeNameToID(targetName)
	return p.nameToID(targetName, "name", committees.metadata)
end

function p.getMember(rep_id)
    return members[rep_id]
end


-- ============================================================================
-- BALANCE OF POWER
-- Returns: { Democratic = int, Republican = int, Vacant = int, [Other] = int }
-- or similar for other countries
-- ============================================================================
function p.balanceOfPower()
    local counts = { Vacant = 0 }
    
    for _, data in pairs(seats) do
		if data.nonvoting == false then
			if data.rep_id == "VACANT" then
				counts.Vacant = counts.Vacant + 1
			else
				local rep = members[data.rep_id]
				if rep then
					-- The universal fallback: uses caucus if it exists, otherwise legal party -- TODO: of dubious utility
					local bloc = rep.party_caucus or rep.party or "Other"
					counts[bloc] = (counts[bloc] or 0) + 1
				end
			end
		end
    end
    
    return counts
end


-- ============================================================================
-- REPRESENTATIVE TO DISTRICT
-- Returns: Array of matching districts (e.g., {{"LA", 4}} in the case of the US)
-- in case one rep occupies several districts for some reason
-- ============================================================================
-- Core: Takes a precise QID and returns matching district strings
function p.repIDToSeatID(rep_id)
    -- if not targetId then return {} end
    local matches = {}
    
    for seat_id, data in pairs(seats) do
		if data.rep_id == rep_id then
			table.insert(matches, seat_id)
		end
    end
	
	if #matches > 1 then
		error("rep id matched more than one seat")
	end
	if #matches == 0 then
		return nil
	end
	return matches[1]
end

-- Wrapper: Accepts a name, resolves it to a QID, and executes the core search
function p.repToSeatID(rep_name)
    local rep_id = p.repNameToID(rep_name)
    -- if rep_id == nil then return {} end
    return p.repIDToSeatID(rep_id)
end


-- ============================================================================
-- DISTRICT TO REPRESENTATIVE (O(1) Direct Lookup)
-- Returns: The raw member data dictionary for that specific seat
-- ============================================================================
-- Core: O(1) lookup returning only the QID
function p.seatToRepID(seat_id)
    -- if not state or not dist then return nil end
    -- if not seat_data then return nil end
    return seats[seat_id].rep_id
end

-- Wrapper: Returns the full biographical data dictionary
function p.seatToRep(seat_id)
    local rep_id = p.seatToRepID(seat_id)
    -- if not rep_id or rep_id == "VACANT" then return nil end
    if rep_id == "VACANT" then return nil end
    return members[rep_id]
end


-- ============================================================================
-- LIST ALL CAUCUSES
-- Returns: Array of unique caucus name strings
-- ============================================================================
function p.listAllCaucuses()
    local unique = {}
    for _, data in pairs(members) do
        if data.other_caucuses then
            for cName in pairs(data.other_caucuses) do
                unique[cName] = true
            end
        end
    end
    
    local out = {}
    for k in pairs(unique) do table.insert(out, k) end
    table.sort(out)
    return out
end


-- ============================================================================
-- LIST ALL COMMITTEES AND SUBCOMMITTEES (NESTED)
-- Returns: {[committeeName] = {[subcommitteeName] = true}}
-- ============================================================================
function p.listAllCommittees() -- TODO: this should be broken into 2 functions
    local tree = {}
    for code, meta in pairs(committees.metadata) do
        -- Identifies parent committees by the "00" suffix in the schema
        if code:sub(-2) == "00" then
            tree[meta.name] = {}
            if meta.subcommittees then
                for _, sub_code in ipairs(meta.subcommittees) do
                    local sub_meta = committees.metadata[sub_code]
                    if sub_meta then
                        tree[meta.name][sub_meta.name] = true
                    end
                end
            end
        end
    end
    return tree
end


-- ============================================================================
-- LIST A REP'S COMMITTEE ASSIGNMENTS
-- Returns: The member's specific committees dictionary containing ranks/subs
-- ============================================================================
-- Core: Enumerate committees by QID
function p.repIDToCommittees(rep_id)
    -- if not targetId then return nil end
    local out = {}
    
    for code, roster in pairs(committees.rosters) do
        for _, member_info in ipairs(roster) do
			if member_info.rep_id == rep_id then
				out[code] = member_info
				break
			end
		end
	end
    
	return out
end

-- Wrapper: Enumerate committees by Name
function p.repToCommittees(rep_name)
    local rep_id = p.repNameToID(rep_name)
    return p.repIDToCommittees(rep_id)
end


-- ============================================================================
-- LIST A REP'S CAUCUS MEMBERSHIPS
-- Returns: { party_caucus = str, other_caucuses = {[caucusName] = {leadership = str/nil}} }
-- ============================================================================
-- Core: Enumerate caucuses by QID
function p.repIDToCaucuses(rep_id)
	local targetId = rep_id
    -- if not targetId or not members[targetId] then return nil end
    local data = members[targetId]
    return {
		bloc = data.party_caucus or data.party,
        other_caucuses = data.other_caucuses or {}
    }
end

-- Wrapper: Enumerate caucuses by Name
function p.repToCaucuses(targetName)
    local rep_id = p.repNameToID(targetName)
    return p.repIDToCaucuses(rep_id)
end


-- ============================================================================
-- LIST A COMMITTEE'S MAJORITY AND MINORITY MEMBERS
-- Returns: { majority = {{name=str, dist=str, rank=int}}, minority = {{...}} }
-- ============================================================================
function p.committeeIDToRoster(committeeID)
	return committees.rosters[committeeID]
end

-- function p.committeeToRoster(committee_name)
function p.committeeToMembers(committee_name)
	return p.committeeIDToRoster(p.committeeNameToID(committee_name))
end

-- ultimate formatting function that outputs a wikitable (it wraps the specific function thence)
function p.committeeRoster(frame)
    local args = p.getArgs(frame)
    
    -- 1. Build paths and load data universally
    local country_code = p.initEnvironment(args)

	-- TODO: have a standard one where it just lists them if such a function doesn't exist
    -- 2. Dynamically load the country's specific formatter (e.g., Module:Legislature/US)
    local country_formatter = require(base_module_name .. "/" .. country_code).formatCommitteeRosterTable
    
    -- 3. Pass control to the country's custom formatting function
    return country_formatter(frame, p)
end

-- TODO: use country-specific functions to flatten and count this in a general way if necessary, but i think it shouldn't be necessary
-- TODO: check party/bloc of each member by looking up each member if this becomes desired
-- TODO: add flag for nonvoting
function p.committeeToTallies(committee_name)
	local targetCommittee = committee_name
    
    local membersList = p.committeeToMembers(targetCommittee)
    -- local count_majority = 0
	-- local count_minority = 0
	local counts = {}
    for _, member in ipairs(membersList) do
		if counts[member.party_status] == nil then
			counts[member.party_status] = 1
		else
			counts[member.party_status] = counts[member.party_status] + 1
		end
		-- count = count + 1
    end
    
    return counts -- + senateOffset
end

function p.committeeTally(frame)
    local args = p.getArgs(frame)
    -- local country_code = p.initEnvironment(args)
	p.initEnvironment(args)
    local targetCommittee = args.committee or args[1]
	local bloc = args.bloc or args.party or nil
	
	local tallies = p.committeeToTallies(targetCommittee)
	
	if bloc ~= nil then
		return tallies[bloc]
	end
	return (function(t) local sum = 0 for _, value in pairs(t) do sum = sum + value end return sum end)(tallies)
end


-- ============================================================================
-- LIST A CAUCUS'S MEMBERS
-- ============================================================================
-- function p.caucusIDToMembers(caucus_id)
--     pass
-- end

function p.caucusToMembers(caucus_name)
	local list = {}

	for rep_id, rep in pairs(members) do
		local isMember = false
		local leadership = nil
		
		if rep.party_caucus == caucus_name then
			isMember = true
		elseif rep.other_caucuses ~= nil and rep.other_caucuses[caucus_name] ~= nil then
			isMember = true
			leadership = rep.other_caucuses[caucus_name].leadership
		end

		if isMember then
			local to_add = {[rep_id] = {}}
			if leadership ~= nil then
				to_add[rep_id]["leadership"] = leadership
			end
			table.insert(list, to_add)
		end
    end
    
    return list
end


-- ==========================================
-- Function: Get Caucus Tally (Returns integer only)
-- ==========================================
function p.caucusTally(frame)
    local args = p.getArgs(frame)
	p.initEnvironment(args)
    local targetCaucus = args.caucus or args[1]
    -- if not targetCaucus then return 0 end
    
    local doCountVotingRepsOnly = (args.voting_only == "yes")
    -- local senateOffset = tonumber(args.senate) or 0
    
    local membersList = p.caucusToMembers(targetCaucus)
    local count = 0
    
    for _, member in ipairs(membersList) do
        if not (doCountVotingRepsOnly and member.nonvoting) then
            count = count + 1
        end
    end
    
    return count -- + senateOffset
end

-- -- ==========================================
-- -- Function: Get Caucus Members
-- -- ==========================================
-- function p.getCaucusMembers(frame)
--     local args = getArgs(frame)
--     local targetCaucus = args.caucus or args[1]
    
--     if not targetCaucus then
--         return "Error: No caucus specified."
--     end

--     local membersList = {}
--     local count = 0

--     for state, dists in pairs(seats) do
--         for distId, data in pairs(dists) do
--             if data.rep_id ~= "VACANT" then
--                 local rep = members[data.rep_id]
--                 if rep and rep.other_caucuses and rep.other_caucuses[targetCaucus] then
--                     local listEntry = rep.rep_name .. " (" .. state .. "-" .. tostring(distId) .. ")"
--                     table.insert(membersList, listEntry)
--                     count = count + 1
--                 end
--             end
--         end
--     end

--     table.sort(membersList)
--     local header = "'''" .. targetCaucus .. " (" .. count .. " members)'''\n"
    
--     if count == 0 then
--         return header .. "* None"
--     end

--     return header .. "* " .. table.concat(membersList, "\n* ")
-- end

-- -- ==========================================
-- -- Function: Get Committee/Subcommittee Roster & Tally
-- -- ==========================================
-- function p.getCommitteeRoster(frame)
--     local args = getArgs(frame)
--     local committee = args.committee or args[1]
--     local subcommittee = args.subcommittee
    
--     if not committee then return "Error: Committee name required." end
    
--     local targetCode
--     for code, meta in pairs(committees.metadata) do
--         if subcommittee then
--             if meta.name == subcommittee then
--                 local parentCode = code:sub(1, 2) .. "00"
--                 if committees.metadata[parentCode] and committees.metadata[parentCode].name == committee then
--                     targetCode = code
--                     break
--                 end
--             end
--         else
--             if meta.name == committee and code:sub(-2) == "00" then
--                 targetCode = code
--                 break
--             end
--         end
--     end
    
--     if not targetCode then return "Error: Committee or Subcommittee not found." end

--     local repToDist = {}
--     for state, dists in pairs(seats) do
--         for distId, data in pairs(dists) do
--             if data.rep_id ~= "VACANT" then
--                 repToDist[data.rep_id] = state .. "-" .. tostring(distId)
--             end
--         end
--     end

--     local roster = {}
--     local tallies = { Democratic = 0, Republican = 0, Vacant = 0 }
    
--     for _, mem in ipairs(committees.rosters[targetCode] or {}) do
--         if mem.rep_id == "VACANT" then
--             tallies.Vacant = tallies.Vacant + 1
--         else
--             local rep = members[mem.rep_id]
--             if rep then
--                 local party = rep.party
--                 tallies[party] = (tallies[party] or 0) + 1
                
--                 local role = ""
--                 if mem.rank == 1 and mem.party_status == "majority" then
--                     role = " (Chair)"
--                 elseif mem.rank == 1 and mem.party_status == "minority" then
--                     role = " (Ranking Member)"
--                 end
                
--                 local distStr = repToDist[mem.rep_id] or "Unknown"
--                 table.insert(roster, rep.rep_name .. " (" .. distStr .. " - " .. party .. ")" .. role)
--             end
--         end
--     end
    
--     table.sort(roster)
--     local title = committee .. (subcommittee and (" — " .. subcommittee) or "")
--     local summary = string.format("'''%s''' (D: %d, R: %d, Vacant: %d)\n", title, tallies.Democratic, tallies.Republican, tallies.Vacant)
    
--     return summary .. "* " .. table.concat(roster, "\n* ")
-- end

-- -- ==========================================
-- -- Function: Get Representative of a District (O(1) Direct Lookup)
-- -- ==========================================
-- function p.getDistrictRep(frame)
--     local args = getArgs(frame)
--     local targetDistrict = args.district or args[1]
--     if not targetDistrict then return "Error: District required (e.g., CA-12)." end
    
--     local state, dist = targetDistrict:match("^(%u%u)%-(.+)$")
--     if not state or not dist then return "Error: Invalid district format." end
    
--     local dKey = tonumber(dist) or dist
--     local seatData = seats[state] and seats[state][dKey]
    
--     if not seatData then return "Error: District not found." end
    
--     if seatData.rep_id == "VACANT" then
--         return "The seat for " .. targetDistrict .. " is currently vacant."
--     end
    
--     local rep = members[seatData.rep_id]
--     if not rep then return "Error: Member data missing." end
    
--     return rep.rep_name .. " (" .. rep.party .. ")"
-- end


-- ============================================================================
-- TEST SUITE
-- ============================================================================
function p.runTests()
    local out = {}
    
    local function executeTest(testName, result)
        table.insert(out, "=== " .. testName .. " ===")
        table.insert(out, mw.dumpObject(result))
    end

    executeTest("1. Balance of Power", p.balanceOfPower())
    -- executeTest("2. Roster by State ('LA')", p.stateToRoster("LA"))
    executeTest("3. Rep to District ('Mike Johnson')", p.repToSeatID("Mike Johnson"))
    executeTest("4. District to Rep ('LA', 4)", p.seatToRep("LA-4"))
    executeTest("5. Enumerate All Caucuses", p.listAllCaucuses())
    executeTest("6. Enumerate All Committees", p.listAllCommittees())
    executeTest("7. Rep Committees ('Maxwell Frost')", p.repToCommittees("Maxwell Frost"))
    executeTest("8. Rep Caucuses ('Mike Johnson')", p.repToCaucuses("Mike Johnson"))
    executeTest("9. Committee Members ('Committee on Armed Services')", p.committeeToMembers("Committee on Armed Services"))
    executeTest("10. Caucus Members ('Blue Dog Coalition')", p.caucusToMembers("Blue Dog Coalition"))
    executeTest("11. Caucus tally ('Blue Dog Coalition')", p.caucusTally("Blue Dog Coalition"))

    return table.concat(out, "\n\n")
end

return p