Spaces:
Sleeping
Sleeping
Commit Β·
651eb52
1
Parent(s): 5e4d279
Update UI aesthetics and add smart JSON scanning
Browse files
app.py
CHANGED
|
@@ -43,6 +43,19 @@ def get_output_file(state: str) -> str:
|
|
| 43 |
def get_excel_file(state: str) -> str:
|
| 44 |
return os.path.join(EXCEL_DIR, f"{_state_prefix(state)}_Schools.xlsx")
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
# ββ HTML builders βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 48 |
|
|
@@ -264,6 +277,20 @@ def _stream(pytest_args: list, state: str, max_retries: int, mode: str):
|
|
| 264 |
yield (_stopped_html(), _phase_html("Stopped by user."), result_rendered,
|
| 265 |
"\n".join(log_lines[-80:]), out_file if os.path.exists(out_file) else None, gr.update(), gr.update(value="βΉ Stopped", variant="secondary"))
|
| 266 |
else:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
yield _emit()
|
| 268 |
|
| 269 |
|
|
@@ -287,6 +314,12 @@ def ui_retry(state, max_retries):
|
|
| 287 |
if not os.path.exists(out):
|
| 288 |
yield _idle_html(), _phase_html(f"β οΈ No output file for {state}. Run Main Scrape first."), "", "", None, gr.update(), gr.update()
|
| 289 |
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
yield from _stream(
|
| 291 |
["pytest", "tests/test_retry_districts_by_category.py", "-v", "-s", "--state", state],
|
| 292 |
state, max_retries, "retry",
|
|
@@ -354,9 +387,9 @@ custom_theme = gr.themes.Soft(
|
|
| 354 |
|
| 355 |
with gr.Blocks(title="KYS Category Scraper", css=css, theme=custom_theme) as app:
|
| 356 |
|
| 357 |
-
gr.Markdown("# π« KYS School Scraper
|
| 358 |
gr.Markdown(
|
| 359 |
-
"Categories
|
| 360 |
elem_id="subtitle",
|
| 361 |
)
|
| 362 |
|
|
@@ -370,10 +403,7 @@ with gr.Blocks(title="KYS Category Scraper", css=css, theme=custom_theme) as app
|
|
| 370 |
download_btn = gr.File(label="β¬οΈ Download JSON", interactive=False)
|
| 371 |
excel_dl_btn = gr.File(label="β¬οΈ Download Excel", interactive=False)
|
| 372 |
|
| 373 |
-
#
|
| 374 |
-
retries_slider = gr.Slider(minimum=1, maximum=10, value=5, step=1,
|
| 375 |
-
label="π Max captcha retry rounds per combo (each round = 2 API calls)")
|
| 376 |
-
|
| 377 |
gr.Markdown("---")
|
| 378 |
|
| 379 |
# ββ Buttons βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -385,13 +415,13 @@ with gr.Blocks(title="KYS Category Scraper", css=css, theme=custom_theme) as app
|
|
| 385 |
|
| 386 |
with gr.Row():
|
| 387 |
with gr.Column(scale=2):
|
| 388 |
-
gr.Markdown("_Starts fresh
|
| 389 |
with gr.Column(scale=2):
|
| 390 |
-
gr.Markdown("
|
| 391 |
with gr.Column(scale=2):
|
| 392 |
-
gr.Markdown("_Converts
|
| 393 |
with gr.Column(scale=1):
|
| 394 |
-
gr.Markdown("
|
| 395 |
|
| 396 |
gr.Markdown("---")
|
| 397 |
|
|
@@ -408,6 +438,11 @@ with gr.Blocks(title="KYS Category Scraper", css=css, theme=custom_theme) as app
|
|
| 408 |
show_label=False, elem_classes=["log-box"],
|
| 409 |
placeholder="Logs appear here during the run...")
|
| 410 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
# ββ Wiring ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 412 |
shared_outputs = [progress_html, phase_html, result_html, log_box, download_btn, retry_btn, status_indicator]
|
| 413 |
|
|
|
|
| 43 |
def get_excel_file(state: str) -> str:
|
| 44 |
return os.path.join(EXCEL_DIR, f"{_state_prefix(state)}_Schools.xlsx")
|
| 45 |
|
| 46 |
+
def check_missed_schools(json_path: str):
|
| 47 |
+
if not os.path.exists(json_path):
|
| 48 |
+
return None
|
| 49 |
+
try:
|
| 50 |
+
import json
|
| 51 |
+
with open(json_path, 'r', encoding='utf-8') as f:
|
| 52 |
+
data = json.load(f)
|
| 53 |
+
if not isinstance(data, list):
|
| 54 |
+
return None
|
| 55 |
+
return sum(1 for item in data if item.get("status") != "success")
|
| 56 |
+
except Exception:
|
| 57 |
+
return None
|
| 58 |
+
|
| 59 |
|
| 60 |
# ββ HTML builders βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 61 |
|
|
|
|
| 277 |
yield (_stopped_html(), _phase_html("Stopped by user."), result_rendered,
|
| 278 |
"\n".join(log_lines[-80:]), out_file if os.path.exists(out_file) else None, gr.update(), gr.update(value="βΉ Stopped", variant="secondary"))
|
| 279 |
else:
|
| 280 |
+
# Post-run scan of JSON file to guarantee state
|
| 281 |
+
failed_count = check_missed_schools(out_file)
|
| 282 |
+
if failed_count is not None:
|
| 283 |
+
if failed_count == 0:
|
| 284 |
+
is_done = True
|
| 285 |
+
has_failures = False
|
| 286 |
+
phase_text = "β
All data retrieved successfully!"
|
| 287 |
+
result_rendered += "<br/><div style='color:green; font-weight:bold;'>File scanned: All schools retrieved.</div>"
|
| 288 |
+
else:
|
| 289 |
+
is_done = True
|
| 290 |
+
has_failures = True
|
| 291 |
+
phase_text = f"β οΈ {failed_count} combos missed. Click 'Run Retry'."
|
| 292 |
+
result_rendered += f"<br/><div style='color:orange; font-weight:bold;'>File scanned: {failed_count} combos still missing.</div>"
|
| 293 |
+
|
| 294 |
yield _emit()
|
| 295 |
|
| 296 |
|
|
|
|
| 314 |
if not os.path.exists(out):
|
| 315 |
yield _idle_html(), _phase_html(f"β οΈ No output file for {state}. Run Main Scrape first."), "", "", None, gr.update(), gr.update()
|
| 316 |
return
|
| 317 |
+
|
| 318 |
+
failed_count = check_missed_schools(out)
|
| 319 |
+
if failed_count == 0:
|
| 320 |
+
yield _idle_html(), _phase_html("β
All data is already retrieved successfully! No need to retry."), "", "", out, gr.update(), gr.update(value="β
ALL DATA RETRIEVED! You can download/export now.", variant="primary")
|
| 321 |
+
return
|
| 322 |
+
|
| 323 |
yield from _stream(
|
| 324 |
["pytest", "tests/test_retry_districts_by_category.py", "-v", "-s", "--state", state],
|
| 325 |
state, max_retries, "retry",
|
|
|
|
| 387 |
|
| 388 |
with gr.Blocks(title="KYS Category Scraper", css=css, theme=custom_theme) as app:
|
| 389 |
|
| 390 |
+
gr.Markdown("# π« KYS School Scraper", elem_id="title")
|
| 391 |
gr.Markdown(
|
| 392 |
+
"<div style='text-align: center; color: var(--body-text-color-subdued);'>Categories 3 Β· 5 Β· 6 Β· 7 Β· 10 Β· 11</div>",
|
| 393 |
elem_id="subtitle",
|
| 394 |
)
|
| 395 |
|
|
|
|
| 403 |
download_btn = gr.File(label="β¬οΈ Download JSON", interactive=False)
|
| 404 |
excel_dl_btn = gr.File(label="β¬οΈ Download Excel", interactive=False)
|
| 405 |
|
| 406 |
+
# (Settings moved to bottom)
|
|
|
|
|
|
|
|
|
|
| 407 |
gr.Markdown("---")
|
| 408 |
|
| 409 |
# ββ Buttons βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 415 |
|
| 416 |
with gr.Row():
|
| 417 |
with gr.Column(scale=2):
|
| 418 |
+
gr.Markdown("<div style='text-align:center;'>_Starts fresh run_</div>")
|
| 419 |
with gr.Column(scale=2):
|
| 420 |
+
gr.Markdown("<div style='text-align:center;'>_Retries failed captchas_</div>")
|
| 421 |
with gr.Column(scale=2):
|
| 422 |
+
gr.Markdown("<div style='text-align:center;'>_Converts to Excel_</div>")
|
| 423 |
with gr.Column(scale=1):
|
| 424 |
+
gr.Markdown("<div style='text-align:center;'>_Stop process_</div>")
|
| 425 |
|
| 426 |
gr.Markdown("---")
|
| 427 |
|
|
|
|
| 438 |
show_label=False, elem_classes=["log-box"],
|
| 439 |
placeholder="Logs appear here during the run...")
|
| 440 |
|
| 441 |
+
# ββ Settings ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 442 |
+
with gr.Accordion("βοΈ Settings", open=False):
|
| 443 |
+
retries_slider = gr.Slider(minimum=1, maximum=10, value=5, step=1,
|
| 444 |
+
label="π Max captcha retry rounds per combo")
|
| 445 |
+
|
| 446 |
# ββ Wiring ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 447 |
shared_outputs = [progress_html, phase_html, result_html, log_box, download_btn, retry_btn, status_indicator]
|
| 448 |
|