Răsfoiți Sursa

Flag non-voting territorial delegates with an explanatory banner

Vote XMLs report territorial delegates' state as "XX"; the Congress.gov
roster has the real territory code (AS/DC/GU/MP/PR/VI). parse.py now
backfills the territory state during the roster merge.

build_members.py sets is_delegate=true for any House member from a
territory and propagates a `dl: true` flag in the manifest. app.js
renders a yellow note on those dashboards explaining that delegates
can vote in committee and on Committee-of-the-Whole amendments but
not on House-floor final passage — so their structural participation
rate is ~2-10%, not absenteeism.

Six delegates now correctly flagged: Aumua Amata Coleman Radewagen (AS),
Eleanor Holmes Norton (DC), James C. Moylan (GU), Kimberlyn King-Hinds
(MP), Pablo José Hernández (PR), Stacey E. Plaskett (VI).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Max 1 lună în urmă
părinte
comite
092ccf7946
5 a modificat fișierele cu 34 adăugiri și 7 ștergeri
  1. 10 0
      build_members.py
  2. 6 6
      data/119/house/roster.json
  3. 0 0
      data/119/manifest.json
  4. 5 0
      parse.py
  5. 13 1
      template/app.js

+ 10 - 0
build_members.py

@@ -37,6 +37,13 @@ def _worker(task):
         chamber = (m.get("chamber") or "").lower()
         recs = [r for r in _WORKER_RECORDS if r.get("chamber") == chamber]
         metrics = analyze.aggregate(recs, mid, m.get("party"), chamber)
+        # Territorial delegates (AS/DC/GU/MP/PR/VI) sit in the House but
+        # cannot vote on final passage — only on Committee-of-the-Whole
+        # amendments — so their participation is structurally low.
+        is_delegate = (
+            (m.get("chamber") or "").lower() == "house"
+            and (m.get("state") or "") in ("AS", "DC", "GU", "MP", "PR", "VI")
+        )
         payload = {
             "id": mid,
             "name": m.get("full_name") or m.get("name"),
@@ -46,6 +53,7 @@ def _worker(task):
             "served_from": m.get("served_from"),
             "served_to": m.get("served_to"),
             "served_partial": bool(m.get("served_partial", False)),
+            "is_delegate": is_delegate,
             "metrics": metrics,
             "_meta": _WORKER_META,
         }
@@ -153,6 +161,8 @@ def main(argv=None):
                 entry["d"] = result["district"]
             if result.get("served_partial"):
                 entry["sp"] = True
+            if result.get("is_delegate"):
+                entry["dl"] = True
             mx = result.get("metrics") or {}
             entry["k"] = {
                 "total": mx.get("total", 0),

+ 6 - 6
data/119/house/roster.json

@@ -2143,7 +2143,7 @@
     "photo_url": "https://www.congress.gov/img/member/67742d980b34857ecc9090e3_200.jpg",
     "served_from": "2025-01-03",
     "served_partial": false,
-    "state": "XX"
+    "state": "PR"
   },
   "I000056": {
     "bioguide": "I000056",
@@ -2495,7 +2495,7 @@
     "photo_url": "https://www.congress.gov/img/member/67742f0a0b34857ecc9090fb_200.jpg",
     "served_from": "2025-01-03",
     "served_partial": false,
-    "state": "XX"
+    "state": "MP"
   },
   "K000405": {
     "bioguide": "K000405",
@@ -3112,7 +3112,7 @@
     "photo_url": "https://www.congress.gov/img/member/m001219_200.jpg",
     "served_from": "2023-01-03",
     "served_partial": false,
-    "state": "XX"
+    "state": "GU"
   },
   "M001220": {
     "bioguide": "M001220",
@@ -3409,7 +3409,7 @@
     "photo_url": "https://www.congress.gov/img/member/116_dg_dc_norton_eleanor_200.jpg",
     "served_from": "1991-01-03",
     "served_partial": false,
-    "state": "XX"
+    "state": "DC"
   },
   "N000188": {
     "bioguide": "N000188",
@@ -3640,7 +3640,7 @@
     "photo_url": "https://www.congress.gov/img/member/116_dg_vi_plaskett_stacey_200.jpg",
     "served_from": "2015-01-03",
     "served_partial": false,
-    "state": "XX"
+    "state": "VI"
   },
   "P000613": {
     "bioguide": "P000613",
@@ -3783,7 +3783,7 @@
     "photo_url": "https://www.congress.gov/img/member/r000600_200.jpg",
     "served_from": "2015-01-03",
     "served_partial": false,
-    "state": "XX"
+    "state": "AS"
   },
   "R000603": {
     "bioguide": "R000603",

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
data/119/manifest.json


+ 5 - 0
parse.py

@@ -260,7 +260,12 @@ def parse_chamber(congress, chamber):
                 "bioguide": bioguide,
                 "lis": entry.get("lis"),
             }
+            # Overwrite vote-derived state with directory state — vote XMLs
+            # report "XX" for territorial delegates (AS/DC/GU/MP/PR/VI).
+            dir_state = entry.get("state")
             if key in roster:
+                if dir_state and roster[key].get("state") in (None, "", "XX"):
+                    roster[key]["state"] = dir_state
                 roster[key].update({k: v for k, v in enrichment.items() if v is not None})
                 roster[key]["served_partial"] = False
             else:

+ 13 - 1
template/app.js

@@ -295,10 +295,22 @@
     els.summary.appendChild(sub);
   }
 
+  var TERRITORY_NAMES = {
+    AS: 'American Samoa', DC: 'the District of Columbia', GU: 'Guam',
+    MP: 'the Northern Mariana Islands', PR: 'Puerto Rico', VI: 'the U.S. Virgin Islands'
+  };
+
   function renderNote(m) {
     var partial = m.served_partial === true;
     var noVotes = !m.metrics || m.metrics.total === 0;
-    if (partial || noVotes) {
+    var isDelegate = m.is_delegate === true;
+    if (isDelegate) {
+      var terr = TERRITORY_NAMES[m.state] || m.state;
+      els.note.textContent = 'Note: This member is the non-voting delegate from ' + terr +
+        '. House delegates may vote in committees and on amendments in the Committee of the Whole, ' +
+        'but cannot vote on final passage on the House floor. Their low participation rate is structural, not absenteeism.';
+      els.note.classList.remove('is-hidden');
+    } else if (partial || noVotes) {
       var endDate = m.served_to || 'present';
       els.note.textContent = 'This member did not cast roll-call votes during the period analyzed (served ' +
         (m.served_from || '?') + ' – ' + endDate + '). The dashboards below reflect that absence.';

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff