Spaces:
Sleeping
Sleeping
Commit ·
137aa75
1
Parent(s): 02114eb
Implemented IsFlagged column check
Browse files
app.py
CHANGED
|
@@ -56,7 +56,7 @@ FILTER_DOMINANT_COUNTY = False # process ALL counties (multi-county / full-stat
|
|
| 56 |
# "csv" -> Master_Dataset.zip, every result bundled as a CSV (faster: no Excel
|
| 57 |
# writing -- use this for very large / state-sized runs)
|
| 58 |
# The individual per-category files are CSV in both modes.
|
| 59 |
-
MASTER_OUTPUT = "
|
| 60 |
|
| 61 |
# How records are processed:
|
| 62 |
# "stream" -> ONE COUNTY AT A TIME. The input is split into per-county temp
|
|
@@ -189,7 +189,7 @@ def _norm_fips(x):
|
|
| 189 |
# values shaped like "05045-FAULKNER" (FIPS digits, a dash, a county name).
|
| 190 |
# Header names are only used as a last-resort fallback.
|
| 191 |
FIPS_COUNTY_PATTERN = r"^\s*\d{1,5}\s*-\s*[A-Za-z]"
|
| 192 |
-
FALLBACK_FIPS_HEADERS = ["** REC FIPS-COUNTY NAME", "** REC FIPS-COUNTY", "County"
|
| 193 |
|
| 194 |
|
| 195 |
def detect_fips_county_column(df, sample_size=1000, min_score=0.5):
|
|
@@ -258,6 +258,22 @@ def ensure_combined_county(df, fips_col, reason, wl, log=None):
|
|
| 258 |
return "County", f"synthesized '<FIPS>-<NAME>' from bare FIPS column '{fips_col}'"
|
| 259 |
|
| 260 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
# =====================================================================
|
| 262 |
# SELF-TEST (startup health check — same logic as the notebook's cell)
|
| 263 |
# =====================================================================
|
|
@@ -657,6 +673,7 @@ def run_pipeline(uploaded_files, reference_file, blank_cities_not_in_list,
|
|
| 657 |
del dfs
|
| 658 |
gc.collect()
|
| 659 |
_step(log, f"Merged shape: {df.shape}")
|
|
|
|
| 660 |
|
| 661 |
# ---- Locate the FIPS-county column by CONTENT --------------------
|
| 662 |
fips_col, fips_reason = detect_fips_county_column(df)
|
|
@@ -1046,6 +1063,7 @@ def process_county(cdf, wl, fips_col, cluster_offset,
|
|
| 1046 |
Cluster_IDs are offset by `cluster_offset` so, processing counties in sorted
|
| 1047 |
order, the numbering matches the whole-dataset ("memory") path."""
|
| 1048 |
ignore = []
|
|
|
|
| 1049 |
fips_col, _r = ensure_combined_county(cdf, fips_col, "", wl, None)
|
| 1050 |
cdf, audit = fips_validate(cdf, blank_cities_not_in_list, ignore, wl=wl, fips_col=fips_col)
|
| 1051 |
|
|
@@ -1221,9 +1239,10 @@ def _split_by_county(uploaded_files, out_dir, log, chunksize=STREAM_CHUNK_ROWS):
|
|
| 1221 |
else:
|
| 1222 |
sample = pd.read_excel(first)
|
| 1223 |
fips_col, reason = detect_fips_county_column(sample)
|
|
|
|
| 1224 |
del sample
|
| 1225 |
if fips_col is None:
|
| 1226 |
-
return None, reason, []
|
| 1227 |
|
| 1228 |
county_paths = {} # county label -> temp path
|
| 1229 |
counter = [0]
|
|
@@ -1255,7 +1274,7 @@ def _split_by_county(uploaded_files, out_dir, log, chunksize=STREAM_CHUNK_ROWS):
|
|
| 1255 |
_step(log, f"Split: routed {total:,} rows into {len(county_paths)} counties")
|
| 1256 |
|
| 1257 |
ordered = sorted(county_paths.items(), key=lambda kv: str(kv[0]))
|
| 1258 |
-
return fips_col, reason, ordered
|
| 1259 |
|
| 1260 |
|
| 1261 |
def run_streaming(uploaded_files, reference_file, blank_cities_not_in_list,
|
|
@@ -1277,11 +1296,15 @@ def run_streaming(uploaded_files, reference_file, blank_cities_not_in_list,
|
|
| 1277 |
return "\n".join(log) + f"\n\nCould not parse reference list: {e}", []
|
| 1278 |
|
| 1279 |
_step(log, "Splitting input by county (streaming)...")
|
| 1280 |
-
fips_col, reason, counties = _split_by_county(uploaded_files, out_dir, log)
|
| 1281 |
if fips_col is None:
|
| 1282 |
return ("\n".join(log) + "\n\nCOLUMN ERROR: could not find a FIPS-county column. "
|
| 1283 |
"Expected values like '05045-FAULKNER'."), []
|
| 1284 |
_step(log, f"FIPS-county column: '{fips_col}' ({reason}) | {len(counties)} counties")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1285 |
|
| 1286 |
# combined output writers
|
| 1287 |
audit_path = out_dir / "FIPS_Validation_Audit_ChangedRecords.csv"
|
|
|
|
| 56 |
# "csv" -> Master_Dataset.zip, every result bundled as a CSV (faster: no Excel
|
| 57 |
# writing -- use this for very large / state-sized runs)
|
| 58 |
# The individual per-category files are CSV in both modes.
|
| 59 |
+
MASTER_OUTPUT = "excel"
|
| 60 |
|
| 61 |
# How records are processed:
|
| 62 |
# "stream" -> ONE COUNTY AT A TIME. The input is split into per-county temp
|
|
|
|
| 189 |
# values shaped like "05045-FAULKNER" (FIPS digits, a dash, a county name).
|
| 190 |
# Header names are only used as a last-resort fallback.
|
| 191 |
FIPS_COUNTY_PATTERN = r"^\s*\d{1,5}\s*-\s*[A-Za-z]"
|
| 192 |
+
FALLBACK_FIPS_HEADERS = ["** REC FIPS-COUNTY NAME", "** REC FIPS-COUNTY", "County"]
|
| 193 |
|
| 194 |
|
| 195 |
def detect_fips_county_column(df, sample_size=1000, min_score=0.5):
|
|
|
|
| 258 |
return "County", f"synthesized '<FIPS>-<NAME>' from bare FIPS column '{fips_col}'"
|
| 259 |
|
| 260 |
|
| 261 |
+
def ensure_flag_columns(df, log=None):
|
| 262 |
+
"""The Backoffice upload keys on RecordKey + IsFlagged + ComesFromMtg2, so the
|
| 263 |
+
final files must carry those flag columns. Data pulled straight from SQL lacks
|
| 264 |
+
IsFlagged / ComesFromMtg2 (the PBIX added them), so create them as FALSE when
|
| 265 |
+
missing. Columns already present (e.g. a PBIX pull) are left untouched."""
|
| 266 |
+
added = []
|
| 267 |
+
for col in ("IsFlagged", "ComesFromMtg2"):
|
| 268 |
+
if col not in df.columns:
|
| 269 |
+
df[col] = False
|
| 270 |
+
added.append(col)
|
| 271 |
+
if added and log is not None:
|
| 272 |
+
log.append(f"Added missing flag column(s) {', '.join(added)} = FALSE "
|
| 273 |
+
f"(not in input; required for the Backoffice upload).")
|
| 274 |
+
return df
|
| 275 |
+
|
| 276 |
+
|
| 277 |
# =====================================================================
|
| 278 |
# SELF-TEST (startup health check — same logic as the notebook's cell)
|
| 279 |
# =====================================================================
|
|
|
|
| 673 |
del dfs
|
| 674 |
gc.collect()
|
| 675 |
_step(log, f"Merged shape: {df.shape}")
|
| 676 |
+
ensure_flag_columns(df, log)
|
| 677 |
|
| 678 |
# ---- Locate the FIPS-county column by CONTENT --------------------
|
| 679 |
fips_col, fips_reason = detect_fips_county_column(df)
|
|
|
|
| 1063 |
Cluster_IDs are offset by `cluster_offset` so, processing counties in sorted
|
| 1064 |
order, the numbering matches the whole-dataset ("memory") path."""
|
| 1065 |
ignore = []
|
| 1066 |
+
ensure_flag_columns(cdf, None)
|
| 1067 |
fips_col, _r = ensure_combined_county(cdf, fips_col, "", wl, None)
|
| 1068 |
cdf, audit = fips_validate(cdf, blank_cities_not_in_list, ignore, wl=wl, fips_col=fips_col)
|
| 1069 |
|
|
|
|
| 1239 |
else:
|
| 1240 |
sample = pd.read_excel(first)
|
| 1241 |
fips_col, reason = detect_fips_county_column(sample)
|
| 1242 |
+
sample_cols = list(sample.columns)
|
| 1243 |
del sample
|
| 1244 |
if fips_col is None:
|
| 1245 |
+
return None, reason, [], sample_cols
|
| 1246 |
|
| 1247 |
county_paths = {} # county label -> temp path
|
| 1248 |
counter = [0]
|
|
|
|
| 1274 |
_step(log, f"Split: routed {total:,} rows into {len(county_paths)} counties")
|
| 1275 |
|
| 1276 |
ordered = sorted(county_paths.items(), key=lambda kv: str(kv[0]))
|
| 1277 |
+
return fips_col, reason, ordered, sample_cols
|
| 1278 |
|
| 1279 |
|
| 1280 |
def run_streaming(uploaded_files, reference_file, blank_cities_not_in_list,
|
|
|
|
| 1296 |
return "\n".join(log) + f"\n\nCould not parse reference list: {e}", []
|
| 1297 |
|
| 1298 |
_step(log, "Splitting input by county (streaming)...")
|
| 1299 |
+
fips_col, reason, counties, sample_cols = _split_by_county(uploaded_files, out_dir, log)
|
| 1300 |
if fips_col is None:
|
| 1301 |
return ("\n".join(log) + "\n\nCOLUMN ERROR: could not find a FIPS-county column. "
|
| 1302 |
"Expected values like '05045-FAULKNER'."), []
|
| 1303 |
_step(log, f"FIPS-county column: '{fips_col}' ({reason}) | {len(counties)} counties")
|
| 1304 |
+
_missing_flags = [c for c in ("IsFlagged", "ComesFromMtg2") if c not in sample_cols]
|
| 1305 |
+
if _missing_flags:
|
| 1306 |
+
_step(log, f"Adding missing flag column(s) {', '.join(_missing_flags)} = FALSE "
|
| 1307 |
+
f"(required for the Backoffice upload)")
|
| 1308 |
|
| 1309 |
# combined output writers
|
| 1310 |
audit_path = out_dir / "FIPS_Validation_Audit_ChangedRecords.csv"
|