saulsolis09 commited on
Commit
1b9c3f5
·
1 Parent(s): 4fbbaed

Added logic to download csv files first then generate Master Excel file

Browse files
Files changed (1) hide show
  1. app.py +101 -62
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 bundle: Excel workbook OR zip of CSVs (see MASTER_OUTPUT) -
981
- _step(log, f"Building master ({MASTER_OUTPUT})...")
982
- if MASTER_OUTPUT == "csv":
983
- # Bundle the already-written CSVs into one zip. No Excel writing -> fast.
984
- master_path = out_dir / "Master_Dataset.zip"
985
- member_names = ["FIPS_Validation_Audit_ChangedRecords.csv", "cluster_df.csv",
986
- "final_cities.csv", "final_zips.csv", "final_census_tracts.csv",
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
- log, outputs = process(local, None)
 
1073
 
 
 
 
 
 
 
 
 
 
1074
  if output_repo and outputs:
1075
  try:
1076
  api = HfApi(token=token)
1077
- stamp = time.strftime("%Y%m%d_%H%M%S")
1078
- for p in outputs:
1079
- api.upload_file(path_or_fileobj=p, path_in_repo=f"runs/{stamp}/{Path(p).name}",
1080
- repo_id=output_repo, repo_type="dataset")
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 upload to '{output_repo}' failed: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- _step(log, f"Building master ({MASTER_OUTPUT})...")
1412
- if MASTER_OUTPUT == "csv":
1413
- master_path = out_dir / "Master_Dataset.zip"
1414
- member_names = ["FIPS_Validation_Audit_ChangedRecords.csv", "cluster_df.csv",
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 process(uploaded_files, reference_file):
1503
- """Dispatcher used by the UI: streaming or in-memory per PROCESS_MODE."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # All settings are fixed (see constants at top). No user-facing options.
1581
- return process(files, reference)
 
 
 
 
 
 
 
 
 
 
 
 
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,