Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
Upload app.py
Browse files
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}"
|