Refresh public Space consistency and detail copy
Browse files
dataset_bundle/evidence_audit/consistency_report.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
{
|
| 2 |
-
"generated_at": "2026-04-
|
| 3 |
"event_provenance": {
|
| 4 |
"event_count": 3918,
|
| 5 |
"events_with_artifacts": 3878,
|
|
|
|
| 1 |
{
|
| 2 |
+
"generated_at": "2026-04-19T20:18:33-04:00",
|
| 3 |
"event_provenance": {
|
| 4 |
"event_count": 3918,
|
| 5 |
"events_with_artifacts": 3878,
|
dataset_bundle/public_release_manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
{
|
| 2 |
"public_version": "congress-public-records-slice-2026-04-v1",
|
| 3 |
"title": "Congress Public Records Slice",
|
| 4 |
-
"release_date": "2026-04-
|
| 5 |
"slice_description": "A neutral, review-oriented slice of House public-record linkages across financial disclosures, sector overlap, and community project funding recipient relationships.",
|
| 6 |
"source_run_name": "house_all_baseline_20260418_v21_recipienthardening",
|
| 7 |
"dataset_repo_id": "cjc0013/cmp-data",
|
|
|
|
| 1 |
{
|
| 2 |
"public_version": "congress-public-records-slice-2026-04-v1",
|
| 3 |
"title": "Congress Public Records Slice",
|
| 4 |
+
"release_date": "2026-04-19T20:19:47-04:00",
|
| 5 |
"slice_description": "A neutral, review-oriented slice of House public-record linkages across financial disclosures, sector overlap, and community project funding recipient relationships.",
|
| 6 |
"source_run_name": "house_all_baseline_20260418_v21_recipienthardening",
|
| 7 |
"dataset_repo_id": "cjc0013/cmp-data",
|
public_space_app.py
CHANGED
|
@@ -842,7 +842,7 @@ def _plain_strengthener(value: str) -> str:
|
|
| 842 |
normalized = str(value or "").strip()
|
| 843 |
mapping = {
|
| 844 |
"bill_sector_mapping_weak": "Requires stronger correlation between the trade window and related bill subject matter.",
|
| 845 |
-
"donor_industry_mapping_weak": "
|
| 846 |
"committee_history_missing": "Committee history is missing or incomplete for this row.",
|
| 847 |
"lobbying_issue_mapping_weak": "Requires clearer mapping between lobbying issue tags and the policy area in this row.",
|
| 848 |
"recipient_identity_ambiguous": "The recipient identity needs a cleaner match before this can be treated as a stronger link.",
|
|
@@ -868,7 +868,7 @@ def _evidence_chip_help(label: str) -> str:
|
|
| 868 |
"annual disclosure": "Annual financial disclosure records support this relationship.",
|
| 869 |
"bill record": "Bill-status records help show legislative activity in the same topic area.",
|
| 870 |
"funding award": "Published federal award records support a funding-recipient link in this slice.",
|
| 871 |
-
"committee roster": "Committee records
|
| 872 |
"vote activity": "Roll-call vote records add legislative activity in the same topic window.",
|
| 873 |
"lobbying activity": "Lobbying filings add public activity in the same issue area.",
|
| 874 |
"member profile": "Member-published profile or committee context contributes to this relationship summary.",
|
|
@@ -881,8 +881,8 @@ def _score_help_text(ranking_mode: str) -> str:
|
|
| 881 |
normalized = str(ranking_mode or "raw").strip().lower()
|
| 882 |
if normalized == "relative":
|
| 883 |
return (
|
| 884 |
-
"
|
| 885 |
-
"
|
| 886 |
)
|
| 887 |
return (
|
| 888 |
"Raw score. It favors clearer public support, more supporting rows, more integrity-checked records, "
|
|
@@ -1017,14 +1017,42 @@ def _collect_pipe_values(frame: pd.DataFrame, column: str, *, limit: int = 20) -
|
|
| 1017 |
return items
|
| 1018 |
|
| 1019 |
|
| 1020 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1021 |
labels = [
|
| 1022 |
_plain_reason_code(item)
|
| 1023 |
-
for item in
|
| 1024 |
-
|
| 1025 |
]
|
| 1026 |
-
if not labels:
|
| 1027 |
-
labels = [_plain_reason_code(item) for item in _split_pipe_values(row.get("reason_codes", ""), limit=20)]
|
| 1028 |
ordered: list[str] = []
|
| 1029 |
for label in labels:
|
| 1030 |
if label and label not in ordered:
|
|
@@ -1032,6 +1060,15 @@ def _relationship_reason_labels(link_rows: pd.DataFrame, event_rows: pd.DataFram
|
|
| 1032 |
return ordered[:10]
|
| 1033 |
|
| 1034 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1035 |
def _relationship_strengtheners(link_rows: pd.DataFrame, event_rows: pd.DataFrame) -> list[str]:
|
| 1036 |
labels = [
|
| 1037 |
_plain_strengthener(item)
|
|
@@ -1216,21 +1253,21 @@ def _relationship_context(
|
|
| 1216 |
link_rows, event_rows = _relationship_constituents(links, events, row)
|
| 1217 |
raw_score = _relationship_score(row)
|
| 1218 |
relative_score = _relative_relationship_score(row, _member_activity_baselines(edges))
|
| 1219 |
-
|
| 1220 |
-
display_score = relative_score if normalized_mode == "relative" else raw_score
|
| 1221 |
surfaced_urls = _select_example_urls(row, link_rows, event_rows, limit=6)
|
| 1222 |
all_urls = [record.get("url", "") for record in sorted(_relationship_source_records(link_rows, event_rows, str(row.get("target_label") or "")), key=lambda item: _source_record_priority(item, str(row.get("target_label") or "")))]
|
| 1223 |
all_urls = [url for url in all_urls if url]
|
| 1224 |
-
|
|
|
|
| 1225 |
strengtheners = _relationship_strengtheners(link_rows, event_rows)
|
| 1226 |
sha_values = _relationship_sha_values(link_rows, event_rows)
|
| 1227 |
-
evidence_chips = _edge_evidence_chips(row, surfaced_urls)
|
| 1228 |
link_type_mix = _relationship_link_type_mix(link_rows)
|
| 1229 |
return {
|
| 1230 |
"row": row,
|
| 1231 |
"raw_score": raw_score,
|
| 1232 |
"relative_score": relative_score,
|
| 1233 |
"display_score": display_score,
|
|
|
|
| 1234 |
"surfaced_urls": surfaced_urls,
|
| 1235 |
"all_urls": all_urls,
|
| 1236 |
"reason_labels": reason_labels,
|
|
@@ -1324,8 +1361,10 @@ def _rank_relationships(edges: pd.DataFrame, ranking_mode: str = "raw") -> pd.Da
|
|
| 1324 |
"member",
|
| 1325 |
"counterparty / sector",
|
| 1326 |
"overall score",
|
|
|
|
| 1327 |
"raw score",
|
| 1328 |
"relative score",
|
|
|
|
| 1329 |
"strength",
|
| 1330 |
"evidence",
|
| 1331 |
"time-window overlap",
|
|
@@ -1351,15 +1390,17 @@ def _rank_relationships(edges: pd.DataFrame, ranking_mode: str = "raw") -> pd.Da
|
|
| 1351 |
chips = _edge_evidence_chips(row)
|
| 1352 |
raw_score = _relationship_score(row)
|
| 1353 |
relative_score = _relative_relationship_score(row, baselines)
|
| 1354 |
-
|
| 1355 |
rows.append(
|
| 1356 |
{
|
| 1357 |
"relationship_id": str(row.get("edge_id") or ""),
|
| 1358 |
"member": str(row.get("member_name") or row.get("member_slug") or ""),
|
| 1359 |
"counterparty / sector": _display_target_label(row),
|
| 1360 |
-
"overall score":
|
|
|
|
| 1361 |
"raw score": raw_score,
|
| 1362 |
"relative score": relative_score,
|
|
|
|
| 1363 |
"status_code": str(row.get("relationship_status", "") or ""),
|
| 1364 |
"strength": _plain_status_label(str(row.get("relationship_status", "") or "")),
|
| 1365 |
"evidence": " | ".join(chips) if chips else "published source support",
|
|
@@ -1372,8 +1413,8 @@ def _rank_relationships(edges: pd.DataFrame, ranking_mode: str = "raw") -> pd.Da
|
|
| 1372 |
}
|
| 1373 |
)
|
| 1374 |
ranked = pd.DataFrame(rows).sort_values(
|
| 1375 |
-
["overall score", "supporting rows", "stronger support", "counterparty / sector"],
|
| 1376 |
-
ascending=[False, False, False, True],
|
| 1377 |
).reset_index(drop=True)
|
| 1378 |
ranked.insert(0, "rank", range(1, len(ranked) + 1))
|
| 1379 |
return ranked
|
|
@@ -1406,7 +1447,8 @@ def _overview_summary_markdown(
|
|
| 1406 |
f"- Showing the top `{min(int(top_n), len(ranked))}` `{_plain_family_label(family).lower()}` for `{focus_label}`.",
|
| 1407 |
f"- Filtered to stronger links only: `{str(bool(only_strong_links)).lower()}`.",
|
| 1408 |
f"- Ranking mode: `{'experimental relative to this member baseline' if str(ranking_mode or 'raw').strip().lower() == 'relative' else 'raw score'}`.",
|
| 1409 |
-
f"- Highest score in this view: `{int(ranked['overall score'].max())}`.",
|
|
|
|
| 1410 |
"- Pick one relationship below to see the evidence breakdown and coarse evidence window.",
|
| 1411 |
]
|
| 1412 |
if not str(member_query or "").strip():
|
|
@@ -1438,7 +1480,9 @@ def _overview_cards_html(
|
|
| 1438 |
f"for <strong>{html.escape(focus_label)}</strong>. "
|
| 1439 |
f"Filtered to stronger links only: <strong>{'yes' if bool(only_strong_links) else 'no'}</strong>. "
|
| 1440 |
f"Ranking mode: <strong>{'experimental relative to this member baseline' if str(ranking_mode or 'raw').strip().lower() == 'relative' else 'raw score'}</strong>. "
|
| 1441 |
-
"Hover over score badges and evidence chips for why they matter.
|
|
|
|
|
|
|
| 1442 |
"</div>"
|
| 1443 |
)
|
| 1444 |
cards: list[str] = []
|
|
@@ -1454,7 +1498,14 @@ def _overview_cards_html(
|
|
| 1454 |
unresolved_refs = int(row.get("unresolved refs", 0) or 0)
|
| 1455 |
raw_score = int(row.get("raw score", 0) or 0)
|
| 1456 |
relative_score = int(row.get("relative score", 0) or 0)
|
|
|
|
| 1457 |
score_note = _score_help_text(ranking_mode)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1458 |
cards.append(
|
| 1459 |
f"""
|
| 1460 |
<div class="result-card">
|
|
@@ -1465,11 +1516,12 @@ def _overview_cards_html(
|
|
| 1465 |
<div class="result-subtitle">For {html.escape(str(row.get("member", "") or ""))} in the {_plain_family_label(family).lower()} view.</div>
|
| 1466 |
</div>
|
| 1467 |
<div class="metric-stack">
|
| 1468 |
-
<span class="score-pill" title="{html.escape(score_note)}">
|
| 1469 |
<span class="strength-pill" title="{html.escape(_plain_status_explainer(str(row.get('status_code', '') or '')))}">{html.escape(str(row.get("strength", "") or ""))}</span>
|
| 1470 |
</div>
|
| 1471 |
</div>
|
| 1472 |
<div class="chip-row">{chip_html or '<span class="chip">published source support</span>'}</div>
|
|
|
|
| 1473 |
<div class="meta-grid">
|
| 1474 |
<div><strong>Evidence window</strong>{html.escape(str(row.get("time-window overlap", "") or ""))}</div>
|
| 1475 |
<div><strong>Supporting rows</strong>{supporting_rows}</div>
|
|
@@ -1477,7 +1529,7 @@ def _overview_cards_html(
|
|
| 1477 |
<div><strong>Needs caution</strong>{needs_caution}</div>
|
| 1478 |
<div><strong>Unresolved refs</strong>{unresolved_refs}</div>
|
| 1479 |
<div><strong>Raw score</strong>{raw_score}</div>
|
| 1480 |
-
<div><strong>Relative
|
| 1481 |
</div>
|
| 1482 |
<div class="result-hint">Use Explain this link below to open the detailed breakdown and export files for this relationship.</div>
|
| 1483 |
</div>
|
|
@@ -1496,7 +1548,7 @@ def _relationship_options(ranked: pd.DataFrame) -> list[tuple[str, str]]:
|
|
| 1496 |
return []
|
| 1497 |
options: list[tuple[str, str]] = []
|
| 1498 |
for row in ranked.to_dict("records"):
|
| 1499 |
-
label = f"#{int(row['rank'])} {row['counterparty / sector']} - {row['strength']} (
|
| 1500 |
options.append((label, str(row["relationship_id"])))
|
| 1501 |
return options
|
| 1502 |
|
|
@@ -1527,15 +1579,16 @@ def _relationship_detail_markdown(
|
|
| 1527 |
urls = context["surfaced_urls"]
|
| 1528 |
raw_score = int(context["raw_score"])
|
| 1529 |
relative_score = int(context["relative_score"])
|
| 1530 |
-
display_score = int(context["display_score"])
|
| 1531 |
lines = [
|
| 1532 |
f"### {row.get('member_name') or row.get('member_slug')} -> {context['display_target_label']}",
|
| 1533 |
"",
|
|
|
|
| 1534 |
f"- Relationship view: `{_plain_family_label(family)}`",
|
| 1535 |
f"- Strength label: `{_plain_status_label(str(row.get('relationship_status', '') or ''))}`",
|
| 1536 |
-
f"-
|
| 1537 |
f"- Raw score: `{raw_score}`",
|
| 1538 |
f"- Relative-to-baseline score (experimental): `{relative_score}`",
|
|
|
|
| 1539 |
f"- Supporting relationship rows: `{int(row.get('link_count', 0) or 0)}`",
|
| 1540 |
f"- Stronger-support rows: `{int(row.get('linked_count', 0) or 0) if family == 'recipient' else int(row.get('strong_event_count', 0) or 0)}`",
|
| 1541 |
f"- Caution / weaker rows: `{int(row.get('review_count', 0) or 0) if family == 'recipient' else int(row.get('weak_event_count', 0) or 0)}`",
|
|
@@ -1548,6 +1601,10 @@ def _relationship_detail_markdown(
|
|
| 1548 |
lines.append(f"- Released row kinds involved: `{'; '.join(context['link_type_mix'])}`")
|
| 1549 |
if context["topic_area_note"]:
|
| 1550 |
lines.append(f"- Topic-area note: {context['topic_area_note']}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1551 |
if reason_codes:
|
| 1552 |
lines.extend(["", "#### Why it is linked in this slice", ""])
|
| 1553 |
lines.extend(f"- {item}" for item in reason_codes)
|
|
@@ -1864,7 +1921,7 @@ def _timeline_window_from_url(url: str) -> tuple[int, str, str]:
|
|
| 1864 |
if "usaspending.gov/award/" in normalized:
|
| 1865 |
return (40, "Published award record", "Federal award record")
|
| 1866 |
if "committee_info" in normalized:
|
| 1867 |
-
return (50, "Current
|
| 1868 |
return (60, "Published source", urlparse(normalized).netloc if normalized.startswith("http") else "Published source")
|
| 1869 |
|
| 1870 |
|
|
|
|
| 842 |
normalized = str(value or "").strip()
|
| 843 |
mapping = {
|
| 844 |
"bill_sector_mapping_weak": "Requires stronger correlation between the trade window and related bill subject matter.",
|
| 845 |
+
"donor_industry_mapping_weak": "More granular industry tagging would improve precision.",
|
| 846 |
"committee_history_missing": "Committee history is missing or incomplete for this row.",
|
| 847 |
"lobbying_issue_mapping_weak": "Requires clearer mapping between lobbying issue tags and the policy area in this row.",
|
| 848 |
"recipient_identity_ambiguous": "The recipient identity needs a cleaner match before this can be treated as a stronger link.",
|
|
|
|
| 868 |
"annual disclosure": "Annual financial disclosure records support this relationship.",
|
| 869 |
"bill record": "Bill-status records help show legislative activity in the same topic area.",
|
| 870 |
"funding award": "Published federal award records support a funding-recipient link in this slice.",
|
| 871 |
+
"committee roster": "Committee records here provide current committee context. They are not presented as exact time-overlap proof.",
|
| 872 |
"vote activity": "Roll-call vote records add legislative activity in the same topic window.",
|
| 873 |
"lobbying activity": "Lobbying filings add public activity in the same issue area.",
|
| 874 |
"member profile": "Member-published profile or committee context contributes to this relationship summary.",
|
|
|
|
| 881 |
normalized = str(ranking_mode or "raw").strip().lower()
|
| 882 |
if normalized == "relative":
|
| 883 |
return (
|
| 884 |
+
"Raw score is still the main public score shown on the card. Experimental relative ordering only changes how the list is sorted "
|
| 885 |
+
"compared with the same member's other visible relationships in the current view."
|
| 886 |
)
|
| 887 |
return (
|
| 888 |
"Raw score. It favors clearer public support, more supporting rows, more integrity-checked records, "
|
|
|
|
| 1017 |
return items
|
| 1018 |
|
| 1019 |
|
| 1020 |
+
def _relationship_reason_codes(link_rows: pd.DataFrame, event_rows: pd.DataFrame, row: Dict[str, Any]) -> list[str]:
|
| 1021 |
+
codes = _collect_pipe_values(link_rows, "reason_codes", limit=20) + _collect_pipe_values(event_rows, "reason_codes", limit=20)
|
| 1022 |
+
if not codes:
|
| 1023 |
+
codes = _split_pipe_values(row.get("reason_codes", ""), limit=20)
|
| 1024 |
+
ordered: list[str] = []
|
| 1025 |
+
for code in codes:
|
| 1026 |
+
normalized = str(code or "").strip()
|
| 1027 |
+
if normalized and normalized not in ordered:
|
| 1028 |
+
ordered.append(normalized)
|
| 1029 |
+
return ordered[:12]
|
| 1030 |
+
|
| 1031 |
+
|
| 1032 |
+
def _reason_visible_in_public_card(reason_code: str, evidence_chips: list[str]) -> bool:
|
| 1033 |
+
chip_set = {str(item or "").strip().lower() for item in evidence_chips}
|
| 1034 |
+
requirements = {
|
| 1035 |
+
"committee_jurisdiction_match": {"committee roster"},
|
| 1036 |
+
"major_vote_overlap": {"vote activity"},
|
| 1037 |
+
"vote_density_support": {"vote activity"},
|
| 1038 |
+
"lobbying_issue_overlap": {"lobbying activity"},
|
| 1039 |
+
"lobbying_density_support": {"lobbying activity"},
|
| 1040 |
+
"bill_sponsor_overlap": {"bill record", "vote activity"},
|
| 1041 |
+
"legislative_relevance_match": {"bill record", "vote activity"},
|
| 1042 |
+
"legislative_density_support": {"bill record", "vote activity"},
|
| 1043 |
+
}
|
| 1044 |
+
required = requirements.get(str(reason_code or "").strip())
|
| 1045 |
+
if not required:
|
| 1046 |
+
return True
|
| 1047 |
+
return bool(chip_set.intersection(required))
|
| 1048 |
+
|
| 1049 |
+
|
| 1050 |
+
def _relationship_reason_labels(link_rows: pd.DataFrame, event_rows: pd.DataFrame, row: Dict[str, Any], evidence_chips: list[str]) -> list[str]:
|
| 1051 |
labels = [
|
| 1052 |
_plain_reason_code(item)
|
| 1053 |
+
for item in _relationship_reason_codes(link_rows, event_rows, row)
|
| 1054 |
+
if _reason_visible_in_public_card(item, evidence_chips)
|
| 1055 |
]
|
|
|
|
|
|
|
| 1056 |
ordered: list[str] = []
|
| 1057 |
for label in labels:
|
| 1058 |
if label and label not in ordered:
|
|
|
|
| 1060 |
return ordered[:10]
|
| 1061 |
|
| 1062 |
|
| 1063 |
+
def _relative_bucket(value: int) -> str:
|
| 1064 |
+
score = int(value or 0)
|
| 1065 |
+
if score >= 70:
|
| 1066 |
+
return "above this member's baseline"
|
| 1067 |
+
if score <= 30:
|
| 1068 |
+
return "below this member's baseline"
|
| 1069 |
+
return "near this member's baseline"
|
| 1070 |
+
|
| 1071 |
+
|
| 1072 |
def _relationship_strengtheners(link_rows: pd.DataFrame, event_rows: pd.DataFrame) -> list[str]:
|
| 1073 |
labels = [
|
| 1074 |
_plain_strengthener(item)
|
|
|
|
| 1253 |
link_rows, event_rows = _relationship_constituents(links, events, row)
|
| 1254 |
raw_score = _relationship_score(row)
|
| 1255 |
relative_score = _relative_relationship_score(row, _member_activity_baselines(edges))
|
| 1256 |
+
display_score = raw_score
|
|
|
|
| 1257 |
surfaced_urls = _select_example_urls(row, link_rows, event_rows, limit=6)
|
| 1258 |
all_urls = [record.get("url", "") for record in sorted(_relationship_source_records(link_rows, event_rows, str(row.get("target_label") or "")), key=lambda item: _source_record_priority(item, str(row.get("target_label") or "")))]
|
| 1259 |
all_urls = [url for url in all_urls if url]
|
| 1260 |
+
evidence_chips = _edge_evidence_chips(row, surfaced_urls)
|
| 1261 |
+
reason_labels = _relationship_reason_labels(link_rows, event_rows, row, evidence_chips)
|
| 1262 |
strengtheners = _relationship_strengtheners(link_rows, event_rows)
|
| 1263 |
sha_values = _relationship_sha_values(link_rows, event_rows)
|
|
|
|
| 1264 |
link_type_mix = _relationship_link_type_mix(link_rows)
|
| 1265 |
return {
|
| 1266 |
"row": row,
|
| 1267 |
"raw_score": raw_score,
|
| 1268 |
"relative_score": relative_score,
|
| 1269 |
"display_score": display_score,
|
| 1270 |
+
"relative_bucket": _relative_bucket(relative_score),
|
| 1271 |
"surfaced_urls": surfaced_urls,
|
| 1272 |
"all_urls": all_urls,
|
| 1273 |
"reason_labels": reason_labels,
|
|
|
|
| 1361 |
"member",
|
| 1362 |
"counterparty / sector",
|
| 1363 |
"overall score",
|
| 1364 |
+
"sort score",
|
| 1365 |
"raw score",
|
| 1366 |
"relative score",
|
| 1367 |
+
"relative view",
|
| 1368 |
"strength",
|
| 1369 |
"evidence",
|
| 1370 |
"time-window overlap",
|
|
|
|
| 1390 |
chips = _edge_evidence_chips(row)
|
| 1391 |
raw_score = _relationship_score(row)
|
| 1392 |
relative_score = _relative_relationship_score(row, baselines)
|
| 1393 |
+
sort_score = relative_score if normalized_mode == "relative" else raw_score
|
| 1394 |
rows.append(
|
| 1395 |
{
|
| 1396 |
"relationship_id": str(row.get("edge_id") or ""),
|
| 1397 |
"member": str(row.get("member_name") or row.get("member_slug") or ""),
|
| 1398 |
"counterparty / sector": _display_target_label(row),
|
| 1399 |
+
"overall score": raw_score,
|
| 1400 |
+
"sort score": sort_score,
|
| 1401 |
"raw score": raw_score,
|
| 1402 |
"relative score": relative_score,
|
| 1403 |
+
"relative view": _relative_bucket(relative_score),
|
| 1404 |
"status_code": str(row.get("relationship_status", "") or ""),
|
| 1405 |
"strength": _plain_status_label(str(row.get("relationship_status", "") or "")),
|
| 1406 |
"evidence": " | ".join(chips) if chips else "published source support",
|
|
|
|
| 1413 |
}
|
| 1414 |
)
|
| 1415 |
ranked = pd.DataFrame(rows).sort_values(
|
| 1416 |
+
["sort score", "overall score", "supporting rows", "stronger support", "counterparty / sector"],
|
| 1417 |
+
ascending=[False, False, False, False, True],
|
| 1418 |
).reset_index(drop=True)
|
| 1419 |
ranked.insert(0, "rank", range(1, len(ranked) + 1))
|
| 1420 |
return ranked
|
|
|
|
| 1447 |
f"- Showing the top `{min(int(top_n), len(ranked))}` `{_plain_family_label(family).lower()}` for `{focus_label}`.",
|
| 1448 |
f"- Filtered to stronger links only: `{str(bool(only_strong_links)).lower()}`.",
|
| 1449 |
f"- Ranking mode: `{'experimental relative to this member baseline' if str(ranking_mode or 'raw').strip().lower() == 'relative' else 'raw score'}`.",
|
| 1450 |
+
f"- Highest raw score in this view: `{int(ranked['overall score'].max())}`.",
|
| 1451 |
+
"- `Only stronger links` filters by the overall relationship bucket. A visible card can still include some caution rows inside it.",
|
| 1452 |
"- Pick one relationship below to see the evidence breakdown and coarse evidence window.",
|
| 1453 |
]
|
| 1454 |
if not str(member_query or "").strip():
|
|
|
|
| 1480 |
f"for <strong>{html.escape(focus_label)}</strong>. "
|
| 1481 |
f"Filtered to stronger links only: <strong>{'yes' if bool(only_strong_links) else 'no'}</strong>. "
|
| 1482 |
f"Ranking mode: <strong>{'experimental relative to this member baseline' if str(ranking_mode or 'raw').strip().lower() == 'relative' else 'raw score'}</strong>. "
|
| 1483 |
+
"Hover over score badges and evidence chips for why they matter. "
|
| 1484 |
+
"A card can still include some caution rows here because the stronger-only filter applies to the overall relationship bucket, not every contributing row. "
|
| 1485 |
+
"Pick one relationship below to open the plain-English explanation and evidence window."
|
| 1486 |
"</div>"
|
| 1487 |
)
|
| 1488 |
cards: list[str] = []
|
|
|
|
| 1498 |
unresolved_refs = int(row.get("unresolved refs", 0) or 0)
|
| 1499 |
raw_score = int(row.get("raw score", 0) or 0)
|
| 1500 |
relative_score = int(row.get("relative score", 0) or 0)
|
| 1501 |
+
relative_view = str(row.get("relative view", "") or "")
|
| 1502 |
score_note = _score_help_text(ranking_mode)
|
| 1503 |
+
ranking_mode_note = (
|
| 1504 |
+
f"<div class=\"result-note\"><strong>Experimental relative ordering:</strong> this relationship currently ranks "
|
| 1505 |
+
f"<strong>{html.escape(relative_view)}</strong> ({relative_score}) compared with the same member's other visible links.</div>"
|
| 1506 |
+
if str(ranking_mode or "raw").strip().lower() == "relative"
|
| 1507 |
+
else ""
|
| 1508 |
+
)
|
| 1509 |
cards.append(
|
| 1510 |
f"""
|
| 1511 |
<div class="result-card">
|
|
|
|
| 1516 |
<div class="result-subtitle">For {html.escape(str(row.get("member", "") or ""))} in the {_plain_family_label(family).lower()} view.</div>
|
| 1517 |
</div>
|
| 1518 |
<div class="metric-stack">
|
| 1519 |
+
<span class="score-pill" title="{html.escape(score_note)}">Raw score {raw_score}</span>
|
| 1520 |
<span class="strength-pill" title="{html.escape(_plain_status_explainer(str(row.get('status_code', '') or '')))}">{html.escape(str(row.get("strength", "") or ""))}</span>
|
| 1521 |
</div>
|
| 1522 |
</div>
|
| 1523 |
<div class="chip-row">{chip_html or '<span class="chip">published source support</span>'}</div>
|
| 1524 |
+
{ranking_mode_note}
|
| 1525 |
<div class="meta-grid">
|
| 1526 |
<div><strong>Evidence window</strong>{html.escape(str(row.get("time-window overlap", "") or ""))}</div>
|
| 1527 |
<div><strong>Supporting rows</strong>{supporting_rows}</div>
|
|
|
|
| 1529 |
<div><strong>Needs caution</strong>{needs_caution}</div>
|
| 1530 |
<div><strong>Unresolved refs</strong>{unresolved_refs}</div>
|
| 1531 |
<div><strong>Raw score</strong>{raw_score}</div>
|
| 1532 |
+
<div><strong>Relative view</strong>{html.escape(relative_view)} ({relative_score})</div>
|
| 1533 |
</div>
|
| 1534 |
<div class="result-hint">Use Explain this link below to open the detailed breakdown and export files for this relationship.</div>
|
| 1535 |
</div>
|
|
|
|
| 1548 |
return []
|
| 1549 |
options: list[tuple[str, str]] = []
|
| 1550 |
for row in ranked.to_dict("records"):
|
| 1551 |
+
label = f"#{int(row['rank'])} {row['counterparty / sector']} - {row['strength']} (raw {row['overall score']})"
|
| 1552 |
options.append((label, str(row["relationship_id"])))
|
| 1553 |
return options
|
| 1554 |
|
|
|
|
| 1579 |
urls = context["surfaced_urls"]
|
| 1580 |
raw_score = int(context["raw_score"])
|
| 1581 |
relative_score = int(context["relative_score"])
|
|
|
|
| 1582 |
lines = [
|
| 1583 |
f"### {row.get('member_name') or row.get('member_slug')} -> {context['display_target_label']}",
|
| 1584 |
"",
|
| 1585 |
+
"- This is a lead for inspection, not a claim of wrongdoing, intent, causality, or exact chronology.",
|
| 1586 |
f"- Relationship view: `{_plain_family_label(family)}`",
|
| 1587 |
f"- Strength label: `{_plain_status_label(str(row.get('relationship_status', '') or ''))}`",
|
| 1588 |
+
f"- Public score shown on the card: `{raw_score}`",
|
| 1589 |
f"- Raw score: `{raw_score}`",
|
| 1590 |
f"- Relative-to-baseline score (experimental): `{relative_score}`",
|
| 1591 |
+
f"- Relative view in the current filter set: `{context['relative_bucket']}`",
|
| 1592 |
f"- Supporting relationship rows: `{int(row.get('link_count', 0) or 0)}`",
|
| 1593 |
f"- Stronger-support rows: `{int(row.get('linked_count', 0) or 0) if family == 'recipient' else int(row.get('strong_event_count', 0) or 0)}`",
|
| 1594 |
f"- Caution / weaker rows: `{int(row.get('review_count', 0) or 0) if family == 'recipient' else int(row.get('weak_event_count', 0) or 0)}`",
|
|
|
|
| 1601 |
lines.append(f"- Released row kinds involved: `{'; '.join(context['link_type_mix'])}`")
|
| 1602 |
if context["topic_area_note"]:
|
| 1603 |
lines.append(f"- Topic-area note: {context['topic_area_note']}")
|
| 1604 |
+
if "committee roster" in chips:
|
| 1605 |
+
lines.append(
|
| 1606 |
+
"- Committee context note: committee records shown here provide current reference context and are not part of an exact time-overlap claim."
|
| 1607 |
+
)
|
| 1608 |
if reason_codes:
|
| 1609 |
lines.extend(["", "#### Why it is linked in this slice", ""])
|
| 1610 |
lines.extend(f"- {item}" for item in reason_codes)
|
|
|
|
| 1921 |
if "usaspending.gov/award/" in normalized:
|
| 1922 |
return (40, "Published award record", "Federal award record")
|
| 1923 |
if "committee_info" in normalized:
|
| 1924 |
+
return (50, "Current reference only", "Committee context (not part of the time-overlap claim)")
|
| 1925 |
return (60, "Published source", urlparse(normalized).netloc if normalized.startswith("http") else "Published source")
|
| 1926 |
|
| 1927 |
|