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.

Jump to content

Module:Legislature/US

From Wikipedia, the free encyclopedia
-- local base = require('Module:Legislature')
local base
-- -- Blindly map local package layout into scope
-- local members = mw.loadData('Module:Legislature/US/lower/members')
-- local seats = mw.loadData('Module:Legislature/US/lower/seats')

-- -- Alias the base functions so formatters can see them
-- local qidToWikilink = base.qidToWikilink
-- local getArgs = base.getArgs
local lang = mw.language.getContentLanguage()


local p = {}

p.name = "Congress"
p.current_congress = 119
p.current_session = 2

-- other stuff probably - if there are any truly joint commissions (as opposed to commissions where the House and Senate appoint the members separately), i guess they might go here. not sure about functions.


-- ============================================================================
-- ROSTER BY STATE
-- Returns: Unsorted dictionary of district data for the given state code
-- ============================================================================
function p.stateToRoster(seats, state)
    -- if not state or not seats[state] then return nil end
    return seats[state]
end


-- make this compatible with the universal functions in Module:Legislature
function p.flattenDistricts(seats)
	local dict = {}
	for state_abbr, state_districts in pairs(seats) do
		for district_number, data in pairs(state_districts) do
			dict[state_abbr .. "-" .. tostring(district_number)] = data
		end
	end
	return dict
end

p.getSeats = p.flattenDistricts -- standard name


-- ============================================================================
-- FORMATTING HELPERS
-- ============================================================================
-- Helper: Format a single member line (e.g., "* [[Jim Jordan]], Ohio, ''Chair''")
local function formatMemberLine(member_info, is_majority, committee_bloc_party)
    -- local rep = members[member_info.rep_id]
    -- if not rep then return "" end

	if member_info.former then
		local line = "* " .. member_info.rep_wikilink_and_state
		
	    local mdy = lang:formatDate("F j, Y", member_info.end_date)
		line = line .. " (until " .. mdy .. ")"

		if member_info.note ~= nil then
			line = line .. member_info.note
		end
		
		local party = member_info.party
		if party ~= committee_bloc_party then
			line = "<span {{Party shading/" .. party .. "}}>" .. line .. "</span>"
		end
		
		return line
	end
    
    -- Extract the 2-letter state abbreviation from their seat ID (e.g., "TX" from "TX-15")
    local seat_id = base.repIDToSeatID(member_info.rep_id)
    -- local state_abbr = "Unknown"
	local state_abbr = string.sub(seat_id, 1, 2) -- TODO: this is terrible and should be a function. LLMs should be banned from using string operations
    -- local state_name = stateMap[state_abbr] or state_abbr
	
    local line = base.qidToWikilink(member_info.rep_id) .. ", " .. "{{US state abbreviation to name|" .. state_abbr .. "}}"

	local rep_data = base.getMember(member_info.rep_id)
	local party = rep_data.party
	if party ~= committee_bloc_party then
		line = "<span {{Party shading/" .. party .. "}}>" .. line
	end
	line = "* " .. line
    
    -- Append MVP leadership titles based on rank == 1 - "chair", "vice chair", etc., are in the xml file directly
    if member_info.rank == 1 and not is_majority then
		line = line .. ", ''Ranking Member''"
	elseif member_info.leadership ~= nil then
		line = line .. ", ''" .. member_info.leadership .. "''"
	end

	if member_info.start_date ~= nil then
	    -- Format the date into "Month DD, YYYY"
	    local mdy = lang:formatDate("F j, Y", member_info.start_date)
		line = line .. " (from " .. mdy .. ")"
	end

	-- Append any custom note
    if rep_data.note_committees ~= nil then
        line = line .. rep_data.note_committees
    end

	if member_info.note ~= nil then
		line = line .. member_info.note
	end
	
	if rep_data.party ~= committee_bloc_party then
		line = line .. "</span>"
	end
    
    return line
end

-- ============================================================================
-- WIKITEXT FORMATTERS (Callable by Templates)
-- ============================================================================
function p.formatCommitteeRosterTable(frame, base_module)
	base = base_module
    local args = base.getArgs(frame)
    -- -- Tell the base module to load the data paths passed from the template
    -- base.initEnvironment(args)
	
    local committee_name = args.committee or args[1]
    -- if not committee_name then return "Error: No committee specified." end
    
    -- 1. Call your existing, unmodified data function
    local committee_id = base.committeeNameToID(committee_name)
    local raw_roster = base.committeeIDToRoster(committee_id)
    -- if not raw_roster then return "Error: Committee not found." end

	-- local roster_copy = {}
	-- for _, member_info in ipairs(raw_roster) do
 --        -- Shallow copy the read-only mw.loadData table so it can be mutated
	-- 	local m_copy = {}
 --        for k, v in pairs(member_info) do m_copy[k] = v end
        
 --        -- if m_copy.party_status == "majority" then
 --        --     table.insert(majority_list, m_copy)
 --        -- else
 --        --     assert(m_copy.party_status == "minority")
 --        --     table.insert(minority_list, m_copy)
 --        -- end

	-- 	table.insert(roster_copy, m_copy)
 --    end

	-- raw_roster = roster_copy
    
    -- 2. Sort the roster array into majority and minority groups
    local majority_list = {}
    local minority_list = {}
    
    for _, member_info in ipairs(raw_roster) do
		local m_copy = {}
        for k, v in pairs(member_info) do m_copy[k] = v end

        if m_copy.party_status == "majority" then
            table.insert(majority_list, m_copy)
        -- if member_info.party_status == "majority" then
        --     table.insert(majority_list, member_info)
        -- elseif member_info.party_status == "minority" then
		else
			assert(m_copy.party_status == "minority")
            table.insert(minority_list, m_copy)
			-- assert(member_info.party_status == "minority")
            -- table.insert(minority_list, member_info)
        end
    end

	-- Parse and inject ad hoc former members
    if args.former_members then
        -- Split the string by semicolon to get individual members
        local former_arr = mw.text.split(args.former_members, "%s*;%s*")
        
        for _, fm_str in ipairs(former_arr) do
            -- if fm_str ~= "" then
			-- Split by caret to get the fields
			local parts = mw.text.split(fm_str, "%s*^%s*")
			local rep_wikilink_and_state = parts[1]
			local end_date = parts[2]
		    -- local mdy = lang:formatDate("F j, Y", end_date)
			local party_status = parts[3]
			local party = parts[4] or nil
			local rank = tonumber(parts[5]) or 9999 -- Assign an artificially high rank to force them to the bottom -- TODO: not the standard that is done but rather something the LLM decided was right
			
			-- Build a record
			local member = {
				former = true,
				rep_wikilink_and_state = rep_wikilink_and_state,
				party_status = party_status,
				rank = rank,
				party = party,
				end_date = end_date
				-- note = "(until " .. mdy .. ")"
			}
			
			if party_status == "majority" then
				table.insert(majority_list, member)
			elseif party_status == "minority" then
				table.insert(minority_list, member)
			end
            -- end
        end
    end
    
    -- Sort both lists by their rank integer -- TODO: probably unnecessary since it should already be in order
    table.sort(majority_list, function(a, b) return (a.rank or 999) < (b.rank or 999) end) -- note: i actually don't mind the LLM's defaults here too much
    table.sort(minority_list, function(a, b) return (a.rank or 999) < (b.rank or 999) end)
    
    -- 3. Dynamically determine column party colors based on the Chair and Ranking Member -- TODO: this could be done by just having a field populated in Legislature/US/lower and Legislature/US/upper that tells us who the majority party is - this data is available in the house clerk's xml document - but technically this is more robust because of the possibility of split control and power-sharing and such
	local maj_party = majority_list[1] and base.getMember(majority_list[1].rep_id).party
	local min_party = minority_list[1] and base.getMember(minority_list[1].rep_id).party
    
    -- -- Sort both lists by their rank integer
    -- table.sort(majority_list, function(a, b) return (a.rank or 9999) < (b.rank or 9999) end) -- i don't hate the LLM's defaults here
    -- table.sort(minority_list, function(a, b) return (a.rank or 9999) < (b.rank or 9999) end)

	-- 3.5 Process Ex Officio members if this is a subcommittee

    local parent_id = base.subcommitteeIDToParentCommitteeID(committee_id)
	local ex_maj_line, ex_min_line
	
    if parent_id ~= nil then
        local parent_roster = base.committeeIDToRoster(parent_id)
        local p_maj_chair, p_min_chair

        -- if parent_roster then
		for _, m in ipairs(parent_roster) do
			if m.rank == 1 then
				if m.party_status == "majority" then
					p_maj_chair = m
				elseif m.party_status == "minority" then
					p_min_chair = m
				end
			end
		end
        -- end
		
		local function handleExOfficio(parent_top_ranked_member, subcommittee_list, is_majority, column_party)
            -- if not parent_top_ranked_member then return "" end

            local rep_id = parent_top_ranked_member.rep_id
            
            -- -- Extract the last name from the wikilink target/label -- TODO
            -- local label = mw.wikibase.label(rep_id) or mw.wikibase.sitelink(rep_id) or ""
            -- local last_name = label:match("(%S+)%s*$") or label

			local rep_name = base.getMember(rep_id).rep_name
            local title = is_majority and "Chair" or "Ranking Member"

            -- local formulaic_note = "<ref >As " .. title .. " of the full committee, Rep. " .. rep_name .. " is entitled to sit as an ''ex officio'' member of all subcommittees, but has been designated a full member of this subcommittee.</ref>"
			local ref_name = "exofficio_" .. rep_id
            local full_ref = '<ref name="' .. ref_name .. '">As ' .. title .. ' of the full committee, Rep. ' .. rep_name .. ' is entitled to sit as an \'\'ex officio\'\' member of all subcommittees, but has been designated a full member of this subcommittee.</ref>'
            local short_ref = '<ref name="' .. ref_name .. '" />'

            -- Check if they are already on the subcommittee
            for _, m in ipairs(subcommittee_list) do
                if m.rep_id == rep_id then
                    -- m.note = (m.note or "") .. formulaic_note
                    -- return "* " .. formulaic_note
					m.note = (m.note or "") .. full_ref
                    return "* " .. short_ref
                end
            end

            -- Build a mock member for the standalone Ex Officio line. 
            -- Overriding rank to 999 prevents formatMemberLine from adding subcommittee MVP titles. -- idk what the llm meant by "MVP". maybe like "vice chair" or "chair".
            return formatMemberLine({rep_id = rep_id, rank = 999}, is_majority, column_party)
        end

        ex_maj_line = handleExOfficio(p_maj_chair, majority_list, true, maj_party)
        ex_min_line = handleExOfficio(p_min_chair, minority_list, false, min_party)
    end
    
    -- 4. Format the strings for each member
    local maj_lines = {}
    for _, m in ipairs(majority_list) do
        table.insert(maj_lines, formatMemberLine(m, true, maj_party))
    end
    
    local min_lines = {}
    for _, m in ipairs(minority_list) do
        table.insert(min_lines, formatMemberLine(m, false, min_party))
    end
    
    -- 5. Construct the final Wikitable string
    local out = {}
    table.insert(out, '{| class="wikitable"')
	-- table.insert(out, '|+ Committee roster{{User:Blippy1998/sandbox/HouseClerkXMLCitation}}')
    -- table.insert(out, '! Majority\n! Minority')
    table.insert(out, '! Majority{{User:Blippy1998/sandbox/HouseClerkXMLCitation|name=house_clerk_member_data_xml}}\n! Minority<ref name=house_clerk_member_data_xml/>')
    table.insert(out, '|-')
    
    -- Majority Column
    table.insert(out, '| valign="top" {{Party shading/' .. maj_party .. '}} |')
    table.insert(out, table.concat(maj_lines, "\n"))
    
    -- Minority Column
    table.insert(out, '| valign="top" {{Party shading/' .. min_party .. '}} |')
    table.insert(out, table.concat(min_lines, "\n"))
	
	-- 6. Append Ex Officio Rows (if it's a subcommittee)
    if parent_id ~= nil then
        table.insert(out, '|-')
        table.insert(out, '! colspan="2" | \'\'Ex officio\'\'')
        table.insert(out, '|-')
        
        table.insert(out, '| valign="top" {{Party shading/' .. maj_party .. '}} |')
        table.insert(out, ex_maj_line)
        
        table.insert(out, '| valign="top" {{Party shading/' .. min_party .. '}} |')
        table.insert(out, ex_min_line)
    end
    
    table.insert(out, '|}')
    
    local final_wikitext = table.concat(out, "\n")
	if type(frame) == 'table' and type(frame.preprocess) == 'function' then
        return frame:preprocess(final_wikitext)
    end
    return final_wikitext
end


function p.committeeMeetingPlace(committee)
	local building_codes = {
	    ["CHOB"] = "Cannon House Office Building",    -- https://clerk.house.gov/member_info/MemberData_UserGuide.pdf
	    ["LHOB"] = "Longworth House Office Building", -- https://clerk.house.gov/member_info/MemberData_UserGuide.pdf
	    ["RHOB"] = "Rayburn House Office Building",   -- https://clerk.house.gov/member_info/MemberData_UserGuide.pdf
	    ["FHOB"] = "Ford House Office Building",      -- https://clerk.house.gov/member_info/MemberData_UserGuide.pdf
	    ["OFOB"] = "O'Neill House Office Building",   -- https://clerk.house.gov/member_info/MemberData_UserGuide.pdf
	    ["H"] = "The Capitol",                        -- https://www.govinfo.gov/content/pkg/CDIR-2016-02-12/pdf/CDIR-2016-02-12-HOUSECOMMITTEES.pdf
	    ["C"] = "The Capitol",                        -- https://www.govinfo.gov/content/pkg/CDIR-2016-02-12/pdf/CDIR-2016-02-12-HOUSECOMMITTEES.pdf
	    ["HB"] = "The Capitol",                       -- https://clerk.house.gov/member_info/TTD-119.pdf
	    ["HT"] = "The Capitol",                       -- https://clerk.house.gov/member_info/TTD-119.pdf
	    ["HVC"] = "Capitol Visitor Center",           -- https://clerk.house.gov/member_info/TTD-119.pdf
	    ["CVC"] = "Capitol Visitor Center",           -- https://clerk.house.gov/member_info/TTD-119.pdf
	    ["DSOB"] = "Dirksen Senate Office Building",  -- https://www.senate.gov/visiting/common/generic/building_abbreviation_key.htm
	    ["RSOB"] = "Russell Senate Office Building",  -- https://www.senate.gov/visiting/common/generic/building_abbreviation_key.htm
	    ["HSOB"] = "Hart Senate Office Building"      -- https://www.senate.gov/visiting/common/generic/building_abbreviation_key.htm
	}
	return committee.room .. ", " .. building_codes[committee.building_code] .. "<br/>" .. "Washington, D.C. " .. committee.zip
end


return p