Spaces:
Sleeping
Sleeping
Commit ·
1b9c3f5
1
Parent(s): 4fbbaed
Added logic to download csv files first then generate Master Excel file
Browse files
app.py
CHANGED
|
@@ -670,7 +670,8 @@ def write_master_workbook(path, sheets_in_order, row_limit=1_048_575):
|
|
| 670 |
# MAIN PIPELINE
|
| 671 |
# =====================================================================
|
| 672 |
def run_pipeline(uploaded_files, reference_file, blank_cities_not_in_list,
|
| 673 |
-
filter_dominant_county, cluster_threshold, replace_threshold
|
|
|
|
| 674 |
t_start = time.time()
|
| 675 |
log = []
|
| 676 |
if not uploaded_files:
|
|
@@ -977,33 +978,13 @@ def run_pipeline(uploaded_files, reference_file, blank_cities_not_in_list,
|
|
| 977 |
summary_df.to_csv(summary_path, index=False)
|
| 978 |
outputs.append(str(summary_path))
|
| 979 |
|
| 980 |
-
# ---- Master
|
| 981 |
-
|
| 982 |
-
|
| 983 |
-
|
| 984 |
-
|
| 985 |
-
|
| 986 |
-
|
| 987 |
-
"Summary.csv"]
|
| 988 |
-
members = [out_dir / n for n in member_names if (out_dir / n).exists()]
|
| 989 |
-
members += sorted(out_dir.glob("OutputFile_*.csv")) # full output (chunked)
|
| 990 |
-
with zipfile.ZipFile(master_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 991 |
-
for p in members:
|
| 992 |
-
zf.write(p, arcname=p.name)
|
| 993 |
-
else:
|
| 994 |
-
# Every result on its own sheet; the full output splits across
|
| 995 |
-
# Output_70_95_RapidFuzz_1, _2, ... if it exceeds Excel's row cap.
|
| 996 |
-
master_path = out_dir / "Master_Dataset.xlsx"
|
| 997 |
-
write_master_workbook(master_path, [
|
| 998 |
-
("Output_70_95_RapidFuzz", output_df),
|
| 999 |
-
("ValidationAuditResults", audit),
|
| 1000 |
-
("cluster_df", cluster_df),
|
| 1001 |
-
("final_cities", final_cities),
|
| 1002 |
-
("final_zips", final_zips),
|
| 1003 |
-
("final_census_tracts", final_ct),
|
| 1004 |
-
("Summary", summary_df),
|
| 1005 |
-
])
|
| 1006 |
-
outputs.append(str(master_path))
|
| 1007 |
|
| 1008 |
elapsed_min = (time.time() - t_start) / 60
|
| 1009 |
log.append(f"Done. {len(outputs)} files ready.")
|
|
@@ -1069,20 +1050,41 @@ def run_from_dataset(input_repo, output_repo):
|
|
| 1069 |
except Exception as e:
|
| 1070 |
return f"Failed to download '{fn}' from '{input_repo}': {e}", []
|
| 1071 |
|
| 1072 |
-
|
|
|
|
| 1073 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1074 |
if output_repo and outputs:
|
| 1075 |
try:
|
| 1076 |
api = HfApi(token=token)
|
| 1077 |
-
|
| 1078 |
-
|
| 1079 |
-
|
| 1080 |
-
|
| 1081 |
-
log += (f"\nUploaded {len(outputs)} file(s) to dataset '{output_repo}' "
|
| 1082 |
-
f"under runs/{stamp}/. Retrieve them there:\n"
|
| 1083 |
-
f"https://huggingface.co/datasets/{output_repo}/tree/main/runs/{stamp}")
|
| 1084 |
except Exception as e:
|
| 1085 |
-
log += f"\nWARNING: processed OK but
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1086 |
return log, outputs
|
| 1087 |
|
| 1088 |
|
|
@@ -1322,7 +1324,8 @@ def _split_by_county(uploaded_files, out_dir, log, chunksize=STREAM_CHUNK_ROWS):
|
|
| 1322 |
|
| 1323 |
|
| 1324 |
def run_streaming(uploaded_files, reference_file, blank_cities_not_in_list,
|
| 1325 |
-
filter_dominant_county, cluster_threshold, replace_threshold
|
|
|
|
| 1326 |
"""Per-county streaming pipeline: bounded memory, identical results."""
|
| 1327 |
t_start = time.time()
|
| 1328 |
log = []
|
|
@@ -1407,24 +1410,11 @@ def run_streaming(uploaded_files, reference_file, blank_cities_not_in_list,
|
|
| 1407 |
outputs.append(str(summary_path))
|
| 1408 |
_step(log, f"Summary: {len(summary_df)} county row(s)")
|
| 1409 |
|
| 1410 |
-
# ---- Master ---------
|
| 1411 |
-
|
| 1412 |
-
|
| 1413 |
-
|
| 1414 |
-
|
| 1415 |
-
"final_cities.csv", "final_zips.csv", "final_census_tracts.csv",
|
| 1416 |
-
"Summary.csv"]
|
| 1417 |
-
members = [out_dir / n for n in member_names if (out_dir / n).exists()]
|
| 1418 |
-
members += [Path(p) for p in output_parts]
|
| 1419 |
-
with zipfile.ZipFile(master_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 1420 |
-
for p in members:
|
| 1421 |
-
zf.write(p, arcname=Path(p).name)
|
| 1422 |
-
else:
|
| 1423 |
-
# Stream the big Output sheet from the combined CSV; small sheets load fully.
|
| 1424 |
-
master_path = out_dir / "Master_Dataset.xlsx"
|
| 1425 |
-
_write_master_streaming(master_path, output_parts, audit_path, clu_path,
|
| 1426 |
-
fc_path, fz_path, fct_path, summary_df)
|
| 1427 |
-
outputs.append(str(master_path))
|
| 1428 |
|
| 1429 |
elapsed_min = (time.time() - t_start) / 60
|
| 1430 |
log.append(f"Done. {len(outputs)} files ready.")
|
|
@@ -1499,13 +1489,50 @@ def _write_master_streaming(path, output_parts, audit_path, clu_path,
|
|
| 1499 |
wb.close()
|
| 1500 |
|
| 1501 |
|
| 1502 |
-
def
|
| 1503 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1504 |
if PROCESS_MODE == "stream":
|
| 1505 |
return run_streaming(uploaded_files, reference_file, STRICT_BLANK_CITIES,
|
| 1506 |
-
FILTER_DOMINANT_COUNTY, CLUSTER_THRESHOLD, REPLACE_THRESHOLD
|
|
|
|
| 1507 |
return run_pipeline(uploaded_files, reference_file, STRICT_BLANK_CITIES,
|
| 1508 |
-
FILTER_DOMINANT_COUNTY, CLUSTER_THRESHOLD, REPLACE_THRESHOLD
|
|
|
|
| 1509 |
|
| 1510 |
|
| 1511 |
# =====================================================================
|
|
@@ -1577,8 +1604,20 @@ with gr.Blocks() as demo:
|
|
| 1577 |
files_out = gr.File(label="Output files", file_count="multiple")
|
| 1578 |
|
| 1579 |
def _run(files, reference):
|
| 1580 |
-
#
|
| 1581 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1582 |
|
| 1583 |
run_btn.click(
|
| 1584 |
_run,
|
|
|
|
| 670 |
# MAIN PIPELINE
|
| 671 |
# =====================================================================
|
| 672 |
def run_pipeline(uploaded_files, reference_file, blank_cities_not_in_list,
|
| 673 |
+
filter_dominant_county, cluster_threshold, replace_threshold,
|
| 674 |
+
build_master=True):
|
| 675 |
t_start = time.time()
|
| 676 |
log = []
|
| 677 |
if not uploaded_files:
|
|
|
|
| 978 |
summary_df.to_csv(summary_path, index=False)
|
| 979 |
outputs.append(str(summary_path))
|
| 980 |
|
| 981 |
+
# ---- Master (slow on huge states) -- optional / best-effort ------
|
| 982 |
+
# Built from the CSVs already on disk (same as streaming), so it can be
|
| 983 |
+
# skipped here and run separately after the individual files are secured.
|
| 984 |
+
if build_master:
|
| 985 |
+
_step(log, f"Building master ({MASTER_OUTPUT})...")
|
| 986 |
+
outputs.append(build_master_bundle(out_dir, MASTER_OUTPUT))
|
| 987 |
+
_step(log, "Master built")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 988 |
|
| 989 |
elapsed_min = (time.time() - t_start) / 60
|
| 990 |
log.append(f"Done. {len(outputs)} files ready.")
|
|
|
|
| 1050 |
except Exception as e:
|
| 1051 |
return f"Failed to download '{fn}' from '{input_repo}': {e}", []
|
| 1052 |
|
| 1053 |
+
# Process WITHOUT the master first, so the individual files exist quickly.
|
| 1054 |
+
log, outputs = process(local, None, build_master=False)
|
| 1055 |
|
| 1056 |
+
stamp = time.strftime("%Y%m%d_%H%M%S")
|
| 1057 |
+
dest = f"https://huggingface.co/datasets/{output_repo}/tree/main/runs/{stamp}" if output_repo else ""
|
| 1058 |
+
|
| 1059 |
+
def _upload(paths, api):
|
| 1060 |
+
for p in paths:
|
| 1061 |
+
api.upload_file(path_or_fileobj=p, path_in_repo=f"runs/{stamp}/{Path(p).name}",
|
| 1062 |
+
repo_id=output_repo, repo_type="dataset")
|
| 1063 |
+
|
| 1064 |
+
api = None
|
| 1065 |
if output_repo and outputs:
|
| 1066 |
try:
|
| 1067 |
api = HfApi(token=token)
|
| 1068 |
+
_upload(outputs, api) # <-- SECURE the CSVs first
|
| 1069 |
+
log += (f"\nUploaded {len(outputs)} core file(s) to '{output_repo}' under "
|
| 1070 |
+
f"runs/{stamp}/ BEFORE building the master. If the master step "
|
| 1071 |
+
f"fails, these are already saved.\n{dest}")
|
|
|
|
|
|
|
|
|
|
| 1072 |
except Exception as e:
|
| 1073 |
+
log += f"\nWARNING: processed OK but uploading core files failed: {e}"
|
| 1074 |
+
|
| 1075 |
+
# Master last / best-effort. If it dies on a huge state, the files above survive.
|
| 1076 |
+
if outputs:
|
| 1077 |
+
try:
|
| 1078 |
+
master = build_master_bundle(Path(outputs[0]).parent, MASTER_OUTPUT)
|
| 1079 |
+
outputs.append(master)
|
| 1080 |
+
if api is not None:
|
| 1081 |
+
_upload([master], api)
|
| 1082 |
+
log += f"\nMaster built and uploaded: {Path(master).name}"
|
| 1083 |
+
else:
|
| 1084 |
+
log += f"\nMaster built: {Path(master).name}"
|
| 1085 |
+
except Exception as e:
|
| 1086 |
+
log += (f"\nMaster build/upload FAILED -- the individual files are already "
|
| 1087 |
+
f"saved in the dataset, so nothing is lost: {e}")
|
| 1088 |
return log, outputs
|
| 1089 |
|
| 1090 |
|
|
|
|
| 1324 |
|
| 1325 |
|
| 1326 |
def run_streaming(uploaded_files, reference_file, blank_cities_not_in_list,
|
| 1327 |
+
filter_dominant_county, cluster_threshold, replace_threshold,
|
| 1328 |
+
build_master=True):
|
| 1329 |
"""Per-county streaming pipeline: bounded memory, identical results."""
|
| 1330 |
t_start = time.time()
|
| 1331 |
log = []
|
|
|
|
| 1410 |
outputs.append(str(summary_path))
|
| 1411 |
_step(log, f"Summary: {len(summary_df)} county row(s)")
|
| 1412 |
|
| 1413 |
+
# ---- Master (slow on huge states) -- optional / best-effort ------
|
| 1414 |
+
if build_master:
|
| 1415 |
+
_step(log, f"Building master ({MASTER_OUTPUT})...")
|
| 1416 |
+
outputs.append(build_master_bundle(out_dir, MASTER_OUTPUT))
|
| 1417 |
+
_step(log, "Master built")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1418 |
|
| 1419 |
elapsed_min = (time.time() - t_start) / 60
|
| 1420 |
log.append(f"Done. {len(outputs)} files ready.")
|
|
|
|
| 1489 |
wb.close()
|
| 1490 |
|
| 1491 |
|
| 1492 |
+
def build_master_bundle(out_dir, master_output):
|
| 1493 |
+
"""Build the master (zip or xlsx) from the CSV outputs already on disk in
|
| 1494 |
+
out_dir. Kept SEPARATE from the pipeline so callers can secure the fast CSVs
|
| 1495 |
+
first and run this slow step last / best-effort -- if it times out on a huge
|
| 1496 |
+
state, the individual files are already saved. Returns the master path."""
|
| 1497 |
+
out_dir = Path(out_dir)
|
| 1498 |
+
output_parts = [str(p) for p in sorted(out_dir.glob("OutputFile_*.csv"))]
|
| 1499 |
+
audit_path = out_dir / "FIPS_Validation_Audit_ChangedRecords.csv"
|
| 1500 |
+
clu_path = out_dir / "cluster_df.csv"
|
| 1501 |
+
fc_path = out_dir / "final_cities.csv"
|
| 1502 |
+
fz_path = out_dir / "final_zips.csv"
|
| 1503 |
+
fct_path = out_dir / "final_census_tracts.csv"
|
| 1504 |
+
summary_path = out_dir / "Summary.csv"
|
| 1505 |
+
|
| 1506 |
+
if master_output == "csv":
|
| 1507 |
+
master_path = out_dir / "Master_Dataset.zip"
|
| 1508 |
+
member_names = ["FIPS_Validation_Audit_ChangedRecords.csv", "cluster_df.csv",
|
| 1509 |
+
"final_cities.csv", "final_zips.csv", "final_census_tracts.csv",
|
| 1510 |
+
"Summary.csv"]
|
| 1511 |
+
members = [out_dir / n for n in member_names if (out_dir / n).exists()]
|
| 1512 |
+
members += [Path(p) for p in output_parts]
|
| 1513 |
+
with zipfile.ZipFile(master_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 1514 |
+
for p in members:
|
| 1515 |
+
zf.write(p, arcname=Path(p).name)
|
| 1516 |
+
else:
|
| 1517 |
+
master_path = out_dir / "Master_Dataset.xlsx"
|
| 1518 |
+
summary_df = (pd.read_csv(summary_path, low_memory=False)
|
| 1519 |
+
if summary_path.exists() else pd.DataFrame())
|
| 1520 |
+
_write_master_streaming(master_path, output_parts, audit_path, clu_path,
|
| 1521 |
+
fc_path, fz_path, fct_path, summary_df)
|
| 1522 |
+
return str(master_path)
|
| 1523 |
+
|
| 1524 |
+
|
| 1525 |
+
def process(uploaded_files, reference_file, build_master=True):
|
| 1526 |
+
"""Dispatcher used by the UI: streaming or in-memory per PROCESS_MODE.
|
| 1527 |
+
build_master=False returns just the individual files (no master), so a caller
|
| 1528 |
+
can save/upload those first and build the slow master separately."""
|
| 1529 |
if PROCESS_MODE == "stream":
|
| 1530 |
return run_streaming(uploaded_files, reference_file, STRICT_BLANK_CITIES,
|
| 1531 |
+
FILTER_DOMINANT_COUNTY, CLUSTER_THRESHOLD, REPLACE_THRESHOLD,
|
| 1532 |
+
build_master=build_master)
|
| 1533 |
return run_pipeline(uploaded_files, reference_file, STRICT_BLANK_CITIES,
|
| 1534 |
+
FILTER_DOMINANT_COUNTY, CLUSTER_THRESHOLD, REPLACE_THRESHOLD,
|
| 1535 |
+
build_master=build_master)
|
| 1536 |
|
| 1537 |
|
| 1538 |
# =====================================================================
|
|
|
|
| 1604 |
files_out = gr.File(label="Output files", file_count="multiple")
|
| 1605 |
|
| 1606 |
def _run(files, reference):
|
| 1607 |
+
# Produce the individual files first and show them for download, THEN build
|
| 1608 |
+
# the (slow-on-huge-files) master. If the master step is interrupted, the
|
| 1609 |
+
# files below are already downloadable.
|
| 1610 |
+
log, outputs = process(files, reference, build_master=False)
|
| 1611 |
+
yield log + "\n\nFiles below are ready to download. Building master...", outputs
|
| 1612 |
+
if outputs:
|
| 1613 |
+
try:
|
| 1614 |
+
master = build_master_bundle(Path(outputs[0]).parent, MASTER_OUTPUT)
|
| 1615 |
+
outputs = outputs + [master]
|
| 1616 |
+
yield log + f"\n\nMaster ready: {Path(master).name}", outputs
|
| 1617 |
+
except Exception as e:
|
| 1618 |
+
yield log + f"\n\nFiles are ready. Master build failed: {e}", outputs
|
| 1619 |
+
else:
|
| 1620 |
+
yield log, outputs
|
| 1621 |
|
| 1622 |
run_btn.click(
|
| 1623 |
_run,
|