scripts/loaders/miami/ccd_membership.ts
Served verbatim from the project repository. Internal working document conventions apply: documents may reference file paths, branch names, and findings-ledger anchors from the repo.
// Load NCES CCD school membership (long format) for Miami-Dade →
// enrollment_oct_ccd (the federal copy of the October count, used as the
// independent-publisher check). Race/ethnicity shares pct_* are written only
// with CCD_WRITE_RACE_SHARES=1; the canonical shares come from the state file.
//
// Sources: nces_ccd_membership_<YYYY-YY> (200 MB zips, streamed).
// Run: npx tsx scripts/loaders/miami/ccd_membership.ts [year ...]
import fs from "node:fs";
import {
requireSource, startDataLoad, finishDataLoad, padSchoolNumber, schoolUniverse, upsertFacts, runMain, SOURCES_ROOT, type Fact,
} from "./_lib";
import { streamZipCsv } from "./_zip";
const SCRIPT_PATH = "scripts/loaders/miami/ccd_membership.ts";
const WRITE_RACE_SHARES = process.env.CCD_WRITE_RACE_SHARES === "1";
const RACE_KEYS: Record<string, string> = {
"Black or African American": "pct_black",
"Hispanic/Latino": "pct_hispanic",
"White": "pct_white",
"Asian": "pct_asian",
"Two or more races": "pct_two_or_more",
"American Indian or Alaska Native": "pct_amer_indian",
"Native Hawaiian or Other Pacific Islander": "pct_hawaiian_pac_isl",
};
function fetchedYears(): string[] {
return fs.readdirSync(SOURCES_ROOT)
.filter((d) => /^nces_ccd_membership_\d{4}-\d{2}$/.test(d))
.map((d) => d.replace("nces_ccd_membership_", ""))
.sort();
}
async function loadYear(year: string, universe: Set<string>) {
const sourceId = `nces_ccd_membership_${year}`;
const { filePath, prov } = requireSource(sourceId);
console.log(`[ccd ${year}] streaming ${filePath} …`);
const rows = await streamZipCsv(filePath, /ccd_sch_052_.*\.csv$/i, (r) => r.ST_LEAID === "FL-13");
console.log(`[ccd ${year}] FL-13 rows: ${rows.length}`);
const totals = new Map<string, number>();
const race = new Map<string, Map<string, number>>();
for (const r of rows) {
const num = padSchoolNumber(String(r.ST_SCHID).replace("FL-13-", ""));
if (!num || !universe.has(num)) continue;
const n = Number.parseFloat(r.STUDENT_COUNT);
if (!Number.isFinite(n)) continue;
if (r.TOTAL_INDICATOR === "Education Unit Total") totals.set(num, n);
else if (r.TOTAL_INDICATOR.startsWith("Derived - Subtotal by Race/Ethnicity and Sex")) {
const m = race.get(num) ?? new Map<string, number>();
m.set(r.RACE_ETHNICITY, (m.get(r.RACE_ETHNICITY) ?? 0) + n);
race.set(num, m);
}
}
const facts: Fact[] = [];
for (const [num, t] of totals) {
facts.push({ schoolNumber: num, year, metricKey: "enrollment_oct_ccd", value: t, suppressed: false });
// Race shares (pct_*) come from the same-year FLDOE race file
// (race_membership.ts); the CCD copy is read by the harness as the
// unsuppressed cross-publisher reference (cross_publisher_race.py).
if (!WRITE_RACE_SHARES) continue;
const m = race.get(num);
if (!m) continue;
const denom = [...m.values()].reduce((a, b) => a + b, 0);
if (denom <= 0) continue;
for (const [label, key] of Object.entries(RACE_KEYS)) {
facts.push({ schoolNumber: num, year, metricKey: key, value: ((m.get(label) ?? 0) / denom) * 100, suppressed: false, denominator: denom });
}
}
const load = await startDataLoad({ sourceId, sourceUrl: prov.url, sha256: prov.sha256, bytes: prov.bytes, yearCovered: year, scriptPath: SCRIPT_PATH });
const res = await upsertFacts(facts, load.id);
await finishDataLoad(load.id, { inserted: res.written, updated: 0, suppressed: 0 }, `${totals.size} schools with totals; ${race.size} with race subtotals`);
console.log(`[ccd ${year}] ${res.written} facts for ${totals.size} schools`);
}
async function main() {
const years = process.argv.slice(2).length ? process.argv.slice(2) : fetchedYears();
const universe = await schoolUniverse();
for (const y of years) await loadYear(y, universe);
}
runMain(main);