saulsolis09 commited on
Commit
9be1a26
·
verified ·
1 Parent(s): 955171f

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -0
app.py CHANGED
@@ -74,6 +74,16 @@ STREAM_CHUNK_ROWS = 400_000 # rows per chunk while splitting the input by coun
74
  # on big states, so it's OFF by default. Set True if you actually need that file.
75
  WRITE_RAWFILE = False
76
 
 
 
 
 
 
 
 
 
 
 
77
  # Threshold for the RURAL/URBAN label (records <= this -> RURAL, else URBAN).
78
  RURAL_MAX_RECORDS = 10000
79
 
@@ -1154,6 +1164,69 @@ def list_dataset_inputs(repo_id, token=None):
1154
  return sorted(f for f in files if f.lower().endswith((".csv", ".xlsx", ".xls")))
1155
 
1156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1157
  def run_from_dataset(input_repo, output_repo):
1158
  """Download every supported file from input_repo, run the pipeline, and (if
1159
  output_repo is set) upload the results back to that dataset. Returns
@@ -1194,10 +1267,12 @@ def run_from_dataset(input_repo, output_repo):
1194
  repo_id=output_repo, repo_type="dataset")
1195
 
1196
  api = None
 
1197
  if output_repo and outputs:
1198
  try:
1199
  api = HfApi(token=token)
1200
  _upload(outputs, api) # <-- SECURE the CSVs first
 
1201
  log += (f"\nUploaded {len(outputs)} core file(s) to '{output_repo}' under "
1202
  f"runs/{stamp}/ BEFORE building the master. If the master step "
1203
  f"fails, these are already saved.\n{dest}")
@@ -1218,6 +1293,13 @@ def run_from_dataset(input_repo, output_repo):
1218
  log += (f"\nMaster build/upload FAILED -- the individual files are already "
1219
  f"saved in the dataset, so nothing is lost: {e}")
1220
 
 
 
 
 
 
 
 
1221
  # A clean, clickable list of every file in the dataset, so nobody has to hunt.
1222
  if output_repo and outputs:
1223
  base = f"https://huggingface.co/datasets/{output_repo}/blob/main/runs/{stamp}"
 
74
  # on big states, so it's OFF by default. Set True if you actually need that file.
75
  WRITE_RAWFILE = False
76
 
77
+ # ---- Dataset auto-cleanup (Run from dataset) -------------------------------
78
+ # HF datasets are git repos: deleting a file leaves its bytes in git HISTORY, so
79
+ # storage is only reclaimed by SQUASHING the history. These options keep the two
80
+ # datasets small automatically so you never hit the private-storage quota again.
81
+ # All of this runs ONLY after results have been safely uploaded, so nothing is
82
+ # lost if a run fails.
83
+ DELETE_INPUT_AFTER_RUN = True # delete the processed input file(s) from the input dataset
84
+ KEEP_LATEST_OUTPUT_RUNS = 1 # in the output dataset, keep only the N newest runs/ folders (0 = keep all)
85
+ SQUASH_HISTORY = True # after deleting, squash both datasets' history to reclaim the storage
86
+
87
  # Threshold for the RURAL/URBAN label (records <= this -> RURAL, else URBAN).
88
  RURAL_MAX_RECORDS = 10000
89
 
 
1164
  return sorted(f for f in files if f.lower().endswith((".csv", ".xlsx", ".xls")))
1165
 
1166
 
1167
+ def _cleanup_datasets(api, input_repo, output_repo, processed_inputs, current_stamp):
1168
+ """Keep the datasets small so the private-storage quota is never hit. Runs
1169
+ ONLY after results are safely uploaded. Returns a short log string.
1170
+ Steps (each guarded by its config flag):
1171
+ 1. delete the input file(s) we just processed from the input dataset
1172
+ 2. keep only the newest KEEP_LATEST_OUTPUT_RUNS runs/ folders in the output dataset
1173
+ 3. squash both datasets' git history -- REQUIRED to actually reclaim bytes,
1174
+ since a plain delete leaves the old versions in history."""
1175
+ from huggingface_hub import list_repo_files
1176
+ notes = []
1177
+ squashed_any = False
1178
+
1179
+ # 1) delete processed inputs
1180
+ if DELETE_INPUT_AFTER_RUN and input_repo and processed_inputs:
1181
+ deleted = 0
1182
+ for fn in processed_inputs:
1183
+ try:
1184
+ api.delete_file(path_in_repo=fn, repo_id=input_repo,
1185
+ repo_type="dataset",
1186
+ commit_message=f"Auto-cleanup: remove processed input {fn}")
1187
+ deleted += 1
1188
+ except Exception as e:
1189
+ notes.append(f" (could not delete input '{fn}': {e})")
1190
+ if deleted:
1191
+ notes.append(f" Deleted {deleted} processed input file(s) from '{input_repo}'.")
1192
+
1193
+ # 2) prune old run folders in the output dataset -- keep the newest
1194
+ # KEEP_LATEST_OUTPUT_RUNS folders TOTAL, and the run we just wrote always
1195
+ # counts as one of them (so it is never deleted).
1196
+ if KEEP_LATEST_OUTPUT_RUNS and output_repo:
1197
+ try:
1198
+ files = list_repo_files(output_repo, repo_type="dataset")
1199
+ stamps = sorted({p.split("/")[1] for p in files
1200
+ if p.startswith("runs/") and len(p.split("/")) > 2})
1201
+ others = [s for s in stamps if s != current_stamp] # oldest..newest
1202
+ keep_others = others[-(KEEP_LATEST_OUTPUT_RUNS - 1):] if KEEP_LATEST_OUTPUT_RUNS > 1 else []
1203
+ old = [s for s in others if s not in keep_others]
1204
+ for s in old:
1205
+ try:
1206
+ api.delete_folder(path_in_repo=f"runs/{s}", repo_id=output_repo,
1207
+ repo_type="dataset",
1208
+ commit_message=f"Auto-cleanup: remove old run {s}")
1209
+ except Exception as e:
1210
+ notes.append(f" (could not delete old run '{s}': {e})")
1211
+ if old:
1212
+ notes.append(f" Pruned {len(old)} older run folder(s) from '{output_repo}'.")
1213
+ except Exception as e:
1214
+ notes.append(f" (could not list runs for pruning: {e})")
1215
+
1216
+ # 3) squash history to actually reclaim the storage
1217
+ if SQUASH_HISTORY:
1218
+ for repo in filter(None, {input_repo, output_repo}):
1219
+ try:
1220
+ api.super_squash_history(repo_id=repo, repo_type="dataset")
1221
+ squashed_any = True
1222
+ except Exception as e:
1223
+ notes.append(f" (could not squash history for '{repo}': {e})")
1224
+ if squashed_any:
1225
+ notes.append(" Squashed dataset history -> storage reclaimed.")
1226
+
1227
+ return ("\n\nAuto-cleanup:\n" + "\n".join(notes)) if notes else ""
1228
+
1229
+
1230
  def run_from_dataset(input_repo, output_repo):
1231
  """Download every supported file from input_repo, run the pipeline, and (if
1232
  output_repo is set) upload the results back to that dataset. Returns
 
1267
  repo_id=output_repo, repo_type="dataset")
1268
 
1269
  api = None
1270
+ core_uploaded = False
1271
  if output_repo and outputs:
1272
  try:
1273
  api = HfApi(token=token)
1274
  _upload(outputs, api) # <-- SECURE the CSVs first
1275
+ core_uploaded = True
1276
  log += (f"\nUploaded {len(outputs)} core file(s) to '{output_repo}' under "
1277
  f"runs/{stamp}/ BEFORE building the master. If the master step "
1278
  f"fails, these are already saved.\n{dest}")
 
1293
  log += (f"\nMaster build/upload FAILED -- the individual files are already "
1294
  f"saved in the dataset, so nothing is lost: {e}")
1295
 
1296
+ # Auto-cleanup runs ONLY once results are safely uploaded, so nothing is lost.
1297
+ if api is not None and core_uploaded:
1298
+ try:
1299
+ log += _cleanup_datasets(api, input_repo, output_repo, filenames, stamp)
1300
+ except Exception as e:
1301
+ log += f"\nAuto-cleanup skipped (non-fatal): {e}"
1302
+
1303
  # A clean, clickable list of every file in the dataset, so nobody has to hunt.
1304
  if output_repo and outputs:
1305
  base = f"https://huggingface.co/datasets/{output_repo}/blob/main/runs/{stamp}"