{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2ecba7ed",
   "metadata": {},
   "source": [
    "# Link reported USAspending subawards to prime awards\n",
    "\n",
    "Executed against the September 25, 2026 observation. This is the first 25 rows of a July action-date query, not a complete month. Replay is offline. Reporting timestamps are unavailable in the pinned projection; retrieval times remain separate. Run from the extracted bundle directory."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "68be3dfc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-25T05:35:11.606650Z",
     "iopub.status.busy": "2026-09-25T05:35:11.606268Z",
     "iopub.status.idle": "2026-09-25T05:35:11.628875Z",
     "shell.execute_reply": "2026-09-25T05:35:11.626935Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"source_rows\": 25,\n",
      "  \"linked_rows\": 25,\n",
      "  \"unmatched_rows\": 0,\n",
      "  \"flagged_rows\": 1,\n",
      "  \"prime_awards\": 5,\n",
      "  \"distinct_nonempty_subrecipient_ueis\": 21,\n",
      "  \"has_next_page\": true,\n",
      "  \"retrieved_at\": \"2026-09-25T05:26:23.148440+00:00\"\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "sys.dont_write_bytecode = True\n",
    "import csv, json\n",
    "from pathlib import Path\n",
    "from html import escape\n",
    "from IPython.display import HTML, display\n",
    "from build_edges import build, read_verified\n",
    "summary = build()\n",
    "print(json.dumps(summary, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f2eb15a",
   "metadata": {},
   "source": [
    "## Preserve observation grain and inspect source-backed edges\n",
    "Each row retains its subaward ID. Repeated UEIs are not automatically duplicates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "81b756fd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-25T05:35:11.631882Z",
     "iopub.status.busy": "2026-09-25T05:35:11.631551Z",
     "iopub.status.idle": "2026-09-25T05:35:11.716144Z",
     "shell.execute_reply": "2026-09-25T05:35:11.713882Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table><thead><tr><th>subaward_id</th><th>subrecipient_name</th><th>subrecipient_uei</th><th>prime_piid</th><th>link_status</th></tr></thead><tbody><tr><td>0000000588</td><td>MAX-CEVA SOLUTIONS</td><td>SFZ9FA92HU83</td><td>70CMSD26FR0000060</td><td>linked</td></tr><tr><td>0000000731</td><td>G2S CORP</td><td>R875BEBFLMU4</td><td>75D30122F15135</td><td>linked</td></tr><tr><td>0000000732</td><td>LOYAL SOURCE GOVERNMENT SERVICES LLC</td><td>Y54FQAPG4TN5</td><td>75D30122F15135</td><td>linked</td></tr><tr><td>0000000871</td><td>G2S CORP</td><td>R875BEBFLMU4</td><td>75D30122F15135</td><td>linked</td></tr><tr><td>0000000875</td><td>LOYAL SOURCE GOVERNMENT SERVICES LLC</td><td>Y54FQAPG4TN5</td><td>75D30122F15135</td><td>linked</td></tr><tr><td>0000001584-0</td><td>ADVANCED COOLING TECHNOLOGIES INC</td><td>Z8KVZV3DR7J4</td><td>N0003024C6001</td><td>linked</td></tr><tr><td>0000011731 MOD 3</td><td>RAZORLEAF GOVERNMENT SOLUTIONS LLC</td><td>HR54HX6DF1L5</td><td>N6833525C0136</td><td>linked</td></tr></tbody></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def load(name):\n",
    "    with (Path(\"example-output\") / name).open(newline=\"\", encoding=\"utf-8\") as handle:\n",
    "        return list(csv.DictReader(handle))\n",
    "def show(rows, fields):\n",
    "    header = \"\".join(\"<th>\" + escape(f) + \"</th>\" for f in fields)\n",
    "    body = \"\".join(\"<tr>\" + \"\".join(\"<td>\" + escape(str(row.get(f, \"\"))) + \"</td>\" for f in fields) + \"</tr>\" for row in rows)\n",
    "    display(HTML(\"<table><thead><tr>\" + header + \"</tr></thead><tbody>\" + body + \"</tbody></table>\"))\n",
    "edges = load(\"edges.csv\")\n",
    "show(edges[:7], [\"subaward_id\", \"subrecipient_name\", \"subrecipient_uei\", \"prime_piid\", \"link_status\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ad05428",
   "metadata": {},
   "source": [
    "## Review flags are separate from successful links\n",
    "Do not infer the cause of the date/amount discrepancy, or silently repair it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "da5b2d90",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-25T05:35:11.720468Z",
     "iopub.status.busy": "2026-09-25T05:35:11.719900Z",
     "iopub.status.idle": "2026-09-25T05:35:11.731387Z",
     "shell.execute_reply": "2026-09-25T05:35:11.729537Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table><thead><tr><th>subaward_id</th><th>subaward_action_date</th><th>subaward_amount_usd</th><th>review_flags</th></tr></thead><tbody><tr><td>0000000588</td><td>2026-07-01</td><td>174441.6</td><td>subaward_before_prime_date_signed;prime_zero_obligation</td></tr></tbody></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<table><thead><tr><th>prime_piid</th><th>date_signed</th><th>total_obligation_usd</th><th>retrieved_at</th></tr></thead><tbody><tr><td>70CMSD26FR0000060</td><td>2026-08-26</td><td>0.0</td><td>2026-09-25T05:26:46.882258+00:00</td></tr></tbody></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "flagged = load(\"review-flags.csv\")\n",
    "show(flagged, [\"subaward_id\", \"subaward_action_date\", \"subaward_amount_usd\", \"review_flags\"])\n",
    "primes = load(\"primes.csv\")\n",
    "show([p for p in primes if p[\"prime_award_key\"] == flagged[0][\"prime_award_key\"]], [\"prime_piid\", \"date_signed\", \"total_obligation_usd\", \"retrieved_at\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c73e956a",
   "metadata": {},
   "source": [
    "## Keep financial grains and dates separate\n",
    "Prime obligations appear once per prime table row. The 18 edges for one prime must not multiply its obligations. No combined financial total is produced."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "977e815d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-25T05:35:11.735694Z",
     "iopub.status.busy": "2026-09-25T05:35:11.734799Z",
     "iopub.status.idle": "2026-09-25T05:35:11.745248Z",
     "shell.execute_reply": "2026-09-25T05:35:11.742880Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table><thead><tr><th>prime_piid</th><th>edge_rows</th><th>total_obligation_usd</th></tr></thead><tbody><tr><td>70CMSD26FR0000060</td><td>1</td><td>0.0</td></tr><tr><td>75D30122F15135</td><td>4</td><td>40426653.26</td></tr><tr><td>89233224CNA000004</td><td>18</td><td>3572209101.57</td></tr><tr><td>N0003024C6001</td><td>1</td><td>1219922110.28</td></tr><tr><td>N6833525C0136</td><td>1</td><td>7347313.59</td></tr></tbody></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Reporting timestamps available: 0\n"
     ]
    }
   ],
   "source": [
    "from collections import Counter\n",
    "counts = Counter(row[\"prime_piid\"] for row in edges)\n",
    "show([{\"prime_piid\": p[\"prime_piid\"], \"edge_rows\": counts[p[\"prime_piid\"]], \"total_obligation_usd\": p[\"total_obligation_usd\"]} for p in primes], [\"prime_piid\", \"edge_rows\", \"total_obligation_usd\"])\n",
    "print(\"Reporting timestamps available:\", sum(bool(r[\"reported_at\"]) for r in edges))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2246179",
   "metadata": {},
   "source": [
    "## Validate the observed result\n",
    "An empty unmatched report is valid. Synthetic failure cases live in test_edges.py, not the observed dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a3ac5ead",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-09-25T05:35:11.748815Z",
     "iopub.status.busy": "2026-09-25T05:35:11.748481Z",
     "iopub.status.idle": "2026-09-25T05:35:11.757988Z",
     "shell.execute_reply": "2026-09-25T05:35:11.756316Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Verified: 25 rows preserved, 25 links, 0 unmatched, 1 flagged; more source pages exist.\n"
     ]
    }
   ],
   "source": [
    "source, receipt = read_verified(\"search\")\n",
    "unmatched = load(\"unmatched.csv\")\n",
    "assert len(edges) == len(source[\"results\"]) == 25\n",
    "assert [r[\"subaward_id\"] for r in edges] == [r[\"Sub-Award ID\"] for r in source[\"results\"]]\n",
    "assert len({r[\"observation_id\"] for r in edges}) == 25\n",
    "assert len(primes) == 5 and len(flagged) == 1 and len(unmatched) == 0\n",
    "assert all(r[\"link_status\"] == \"linked\" for r in edges)\n",
    "assert all(not r[\"reported_at\"] and not r[\"last_modified_at\"] for r in edges)\n",
    "assert source[\"page_metadata\"][\"hasNext\"] is True\n",
    "print(\"Verified: 25 rows preserved, 25 links, 0 unmatched, 1 flagged; more source pages exist.\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
