Download app.py from InternScience/ResearchClawBench-Task-Submit: direct link, hf CLI and curl.
- Browser
- Download file 26.8 kB
-
https://huggingface.co/spaces/InternScience/ResearchClawBench-Task-Submit/resolve/main/app.py
- Command line
-
hf download hf://spaces/InternScience/ResearchClawBench-Task-Submit/app.py
-
curl -L -o app.py https://huggingface.co/spaces/InternScience/ResearchClawBench-Task-Submit/resolve/main/app.py
26.8 kB
| from __future__ import annotations | |
| import json | |
| import os | |
| from pathlib import Path | |
| import gradio as gr | |
| from huggingface_hub.utils import HfHubHTTPError | |
| try: | |
| from .repo_ops import ( | |
| DEFAULT_REPO_ID, | |
| allocate_next_task_id, | |
| create_dataset_pr, | |
| get_repo_head_sha, | |
| list_existing_task_ids, | |
| load_hf_token, | |
| ) | |
| from .validator import ( | |
| DOMAINS, | |
| PreparedSubmission, | |
| SubmissionMetadata, | |
| ValidationError, | |
| build_public_report, | |
| cleanup_stale_managed_files, | |
| cleanup_submission_state, | |
| cleanup_uploaded_archive, | |
| cleanup_work_dir, | |
| normalize_domain_token, | |
| persist_uploaded_archive, | |
| stage_submission, | |
| validate_and_prepare_submission, | |
| ) | |
| except ImportError: | |
| from repo_ops import ( | |
| DEFAULT_REPO_ID, | |
| allocate_next_task_id, | |
| create_dataset_pr, | |
| get_repo_head_sha, | |
| list_existing_task_ids, | |
| load_hf_token, | |
| ) | |
| from validator import ( | |
| DOMAINS, | |
| PreparedSubmission, | |
| SubmissionMetadata, | |
| ValidationError, | |
| build_public_report, | |
| cleanup_stale_managed_files, | |
| cleanup_submission_state, | |
| cleanup_uploaded_archive, | |
| cleanup_work_dir, | |
| normalize_domain_token, | |
| persist_uploaded_archive, | |
| stage_submission, | |
| validate_and_prepare_submission, | |
| ) | |
| SPACE_TITLE = 'ResearchClawBench Task Submission' | |
| OFFICIAL_SITE_URL = 'https://internscience.github.io/ResearchClawBench-Home/' | |
| GITHUB_REPO_URL = 'https://github.com/InternScience/ResearchClawBench' | |
| DATASET_URL = f'https://huggingface.co/datasets/{DEFAULT_REPO_ID}' | |
| SPACE_URL = 'https://huggingface.co/spaces/InternScience/ResearchClawBench-Task-Submit' | |
| STATE_TTL_SECONDS = int(os.environ.get('RCB_SPACE_STATE_TTL_SECONDS', '3600')) | |
| STALE_WORK_DIR_TTL_SECONDS = int( | |
| os.environ.get('RCB_SPACE_STALE_WORK_DIR_TTL_SECONDS', str(max(STATE_TTL_SECONDS * 2, 24 * 3600))) | |
| ) | |
| _removed_stale_managed_files = cleanup_stale_managed_files(STALE_WORK_DIR_TTL_SECONDS) | |
| if _removed_stale_managed_files: | |
| print( | |
| f'[startup] Removed {_removed_stale_managed_files} stale managed submission file(s) ' | |
| f'older than {STALE_WORK_DIR_TTL_SECONDS}s.', | |
| flush=True, | |
| ) | |
| CSS = """ | |
| @import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&display=swap'); | |
| :root { | |
| --page-text: #0f172a; | |
| --page-muted: #526075; | |
| --page-line: rgba(15, 23, 42, 0.12); | |
| --page-surface-strong: #ffffff; | |
| } | |
| body { | |
| background: | |
| radial-gradient(circle at top left, rgba(54, 107, 245, 0.12), transparent 34%), | |
| radial-gradient(circle at top right, rgba(15, 118, 110, 0.08), transparent 28%), | |
| linear-gradient(180deg, #f8fafc 0%, #f3f6fb 55%, #f6f8fb 100%); | |
| color: var(--page-text); | |
| } | |
| body, | |
| button, | |
| input, | |
| textarea { | |
| font-family: 'Manrope', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif !important; | |
| } | |
| .gradio-container { | |
| max-width: 1280px !important; | |
| margin: 0 auto !important; | |
| padding: 40px 40px 64px !important; | |
| --block-background-fill: transparent; | |
| --block-border-width: 0px; | |
| --block-border-color: transparent; | |
| --block-label-background-fill: transparent; | |
| --block-label-border-width: 0px; | |
| --panel-background-fill: transparent; | |
| --panel-border-width: 0px; | |
| --panel-border-color: transparent; | |
| --background-fill-secondary: transparent; | |
| --body-background-fill: transparent; | |
| } | |
| .page-shell { | |
| margin-top: 26px; | |
| background: #ffffff; | |
| border: 1px solid rgba(15, 23, 42, 0.08); | |
| border-radius: 22px; | |
| box-shadow: 0 18px 48px rgba(15, 23, 42, 0.05); | |
| overflow: hidden; | |
| padding: 34px 0 40px; | |
| } | |
| .page-shell-content { | |
| gap: 0 !important; | |
| } | |
| .shell-spacer { | |
| min-width: 44px !important; | |
| } | |
| .hero { | |
| padding: 42px 48px 36px; | |
| border-radius: 24px; | |
| color: #f8fbff; | |
| background: | |
| radial-gradient(circle at 14% 18%, rgba(255, 255, 255, 0.16), transparent 18%), | |
| linear-gradient(135deg, #0f274d 0%, #133c7c 46%, #124f75 100%); | |
| box-shadow: 0 26px 60px rgba(15, 39, 77, 0.18); | |
| } | |
| .hero h1 { | |
| margin: 0; | |
| font-size: 2.4rem; | |
| line-height: 1.02; | |
| letter-spacing: -0.04em; | |
| color: #f8fbff !important; | |
| text-shadow: 0 1px 12px rgba(0, 0, 0, 0.14); | |
| } | |
| .hero-copy { | |
| margin-top: 16px; | |
| max-width: 860px; | |
| font-size: 1.04rem; | |
| line-height: 1.72; | |
| color: rgba(248, 251, 255, 0.9) !important; | |
| } | |
| .hero-links { | |
| display: flex; | |
| gap: 14px; | |
| flex-wrap: wrap; | |
| margin-top: 22px; | |
| } | |
| .hero-links a { | |
| color: #f8fbff !important; | |
| text-decoration: none; | |
| font-weight: 700; | |
| letter-spacing: -0.01em; | |
| } | |
| .hero-links a:hover { | |
| text-decoration: underline; | |
| } | |
| .hero-meta { | |
| margin-top: 18px; | |
| font-size: 0.93rem; | |
| color: rgba(248, 251, 255, 0.72) !important; | |
| } | |
| .section-row { | |
| margin-top: 34px; | |
| gap: 30px; | |
| } | |
| .section-row, | |
| .section-row > div, | |
| .section-copy, | |
| .section-copy > div, | |
| .main-form, | |
| .side-notes { | |
| background: transparent !important; | |
| border: 0 !important; | |
| box-shadow: none !important; | |
| } | |
| .section-copy h2 { | |
| margin: 0 0 10px; | |
| font-size: 1.2rem; | |
| letter-spacing: -0.03em; | |
| } | |
| .section-copy h3 { | |
| margin: 24px 0 8px; | |
| font-size: 1rem; | |
| } | |
| .section-copy p, | |
| .section-copy li { | |
| color: #5a667a; | |
| line-height: 1.72; | |
| } | |
| .section-copy ul, | |
| .section-copy ol { | |
| margin: 10px 0 0; | |
| padding-left: 1.2rem; | |
| } | |
| .section-copy code { | |
| font-size: 0.95em; | |
| } | |
| .section-copy .prose { | |
| max-width: 100%; | |
| } | |
| .subtle-block { | |
| padding-bottom: 22px; | |
| border-bottom: 1px solid var(--page-line); | |
| } | |
| .section-copy .prose, | |
| .section-copy .prose *, | |
| .section-copy .md, | |
| .section-copy .md *, | |
| .section-copy .markdown, | |
| .section-copy .markdown * { | |
| background: transparent !important; | |
| } | |
| .main-form { | |
| padding-right: 0; | |
| } | |
| .side-notes { | |
| padding-left: 0; | |
| } | |
| .main-form > div { | |
| padding-right: 18px !important; | |
| } | |
| .side-notes > div { | |
| padding-left: 18px !important; | |
| } | |
| .caption { | |
| margin-top: 8px; | |
| color: var(--page-muted); | |
| font-size: 0.93rem; | |
| line-height: 1.6; | |
| } | |
| .field-label { | |
| margin: 18px 0 8px; | |
| color: var(--page-text); | |
| font-size: 0.95rem; | |
| font-weight: 700; | |
| letter-spacing: -0.01em; | |
| } | |
| .results-shell { | |
| margin-top: 0; | |
| padding-top: 22px; | |
| border-top: 1px solid var(--page-line); | |
| } | |
| .results-shell > div { | |
| margin-top: 26px; | |
| } | |
| .action-row { | |
| margin-top: 10px; | |
| } | |
| .upload-row { | |
| margin-top: 18px; | |
| margin-bottom: 10px; | |
| } | |
| .upload-button button { | |
| border-radius: 12px !important; | |
| min-height: 48px !important; | |
| padding: 0 18px !important; | |
| background: #ffffff !important; | |
| color: var(--page-text) !important; | |
| border: 1px solid rgba(19, 70, 162, 0.16) !important; | |
| box-shadow: 0 8px 22px rgba(15, 23, 42, 0.04) !important; | |
| } | |
| .upload-status { | |
| padding-top: 10px; | |
| } | |
| .upload-status p { | |
| margin: 0 !important; | |
| color: var(--page-muted) !important; | |
| } | |
| .primary-button button, | |
| .secondary-button button { | |
| border-radius: 12px !important; | |
| min-height: 48px !important; | |
| font-weight: 700 !important; | |
| letter-spacing: -0.01em; | |
| } | |
| .primary-button button { | |
| background: linear-gradient(135deg, #1346a2 0%, #155eef 100%) !important; | |
| box-shadow: 0 16px 32px rgba(21, 94, 239, 0.2) !important; | |
| } | |
| .secondary-button button { | |
| background: var(--page-surface-strong) !important; | |
| color: var(--page-text) !important; | |
| border: 1px solid rgba(15, 23, 42, 0.12) !important; | |
| } | |
| .gradio-container .block, | |
| .gradio-container .gr-box, | |
| .gradio-container .gr-form, | |
| .gradio-container .gr-group, | |
| .gradio-container .form, | |
| .gradio-container .input-container, | |
| .gradio-container .wrap, | |
| .gradio-container .row, | |
| .gradio-container .column, | |
| .gradio-container fieldset { | |
| background: transparent !important; | |
| box-shadow: none !important; | |
| border-color: transparent !important; | |
| } | |
| .gradio-container input:not([type="checkbox"]), | |
| .gradio-container textarea, | |
| .gradio-container button[aria-haspopup="listbox"], | |
| .gradio-container button[role="listbox"], | |
| .gradio-container .wrap:has(input:not([type="checkbox"])), | |
| .gradio-container .wrap:has(textarea), | |
| .gradio-container .wrap:has(button[aria-haspopup="listbox"]), | |
| .gradio-container .wrap:has(button[role="listbox"]), | |
| .gradio-container .wrap:has(select), | |
| .gradio-container .input-container:has(input:not([type="checkbox"])), | |
| .gradio-container .input-container:has(textarea), | |
| .gradio-container .input-container:has(button[aria-haspopup="listbox"]), | |
| .gradio-container .input-container:has(button[role="listbox"]), | |
| .gradio-container input:not([type="checkbox"]), | |
| .gradio-container textarea { | |
| background: var(--page-surface-strong) !important; | |
| border: 1px solid rgba(19, 70, 162, 0.16) !important; | |
| border-radius: 10px !important; | |
| box-shadow: 0 1px 0 rgba(15, 23, 42, 0.02), 0 8px 22px rgba(15, 23, 42, 0.04) !important; | |
| } | |
| .gradio-container .block, | |
| .gradio-container .wrap, | |
| .gradio-container .gr-box, | |
| .gradio-container .gr-form, | |
| .gradio-container .gr-panel, | |
| .gradio-container .gr-group, | |
| .gradio-container .form, | |
| .gradio-container .input-container, | |
| .gradio-container .wrap-inner { | |
| overflow: visible !important; | |
| } | |
| .gradio-container label, | |
| .gradio-container .label-wrap, | |
| .gradio-container .caption-label { | |
| color: var(--page-text) !important; | |
| } | |
| @media (max-width: 900px) { | |
| .gradio-container { | |
| padding: 22px 18px 42px !important; | |
| } | |
| .page-shell { | |
| padding: 24px 0 30px; | |
| } | |
| .shell-spacer { | |
| min-width: 18px !important; | |
| } | |
| .hero { | |
| padding: 30px 24px 26px; | |
| border-radius: 20px; | |
| } | |
| .hero h1 { | |
| font-size: 2rem; | |
| } | |
| .main-form, | |
| .side-notes { | |
| padding-right: 0; | |
| padding-left: 0; | |
| } | |
| } | |
| """ | |
| def build_hero_html() -> str: | |
| return f""" | |
| <section class="hero"> | |
| <h1>{SPACE_TITLE}</h1> | |
| <p class="hero-copy"> | |
| Submit a new ResearchClawBench task as a single ZIP archive. This Space validates the full task | |
| structure, checks JSON fields and referenced paths, allocates the next available task ID, and then | |
| opens a PR against the official Hugging Face dataset for maintainer review. | |
| </p> | |
| <div class="hero-links"> | |
| <a href="{OFFICIAL_SITE_URL}" target="_blank">Official Site</a> | |
| <a href="{GITHUB_REPO_URL}" target="_blank">GitHub Repository</a> | |
| <a href="{DATASET_URL}" target="_blank">Hugging Face Dataset</a> | |
| <a href="https://arxiv.org/pdf/2606.07591" target="_blank">arXiv</a> | |
| <a href="{SPACE_URL}" target="_blank">Space Repository</a> | |
| </div> | |
| <div class="hero-meta"> | |
| ZIP upload only · full task-format validation · PR to dataset repo after passing checks | |
| </div> | |
| </section> | |
| """ | |
| def field_label_html(text: str) -> str: | |
| return f'<div class="field-label">{text}</div>' | |
| def submission_guide_markdown() -> str: | |
| return """ | |
| ## Before You Upload | |
| 1. Put exactly one task directory at the top level of the ZIP. | |
| 2. Make sure the directory contains `task_info.json`, `data/`, `related_work/`, and `target_study/`. | |
| 3. Keep every data reference inside `task_info.json` in the `./data/...` format. | |
| 4. Make sure every checklist image path points to `target_study/images/...`. | |
| 5. Ensure that uploaded files can be redistributed through Hugging Face before submitting. | |
| Example task in GitHub: | |
| [tasks/Astronomy_000](https://github.com/InternScience/ResearchClawBench/tree/main/tasks/Astronomy_000) | |
| --- | |
| ## Expected ZIP Layout | |
| ```text | |
| your_submission.zip | |
| └── any_folder_name/ | |
| ├── task_info.json | |
| ├── data/ | |
| ├── related_work/ | |
| │ ├── paper_000.pdf | |
| │ ├── paper_001.pdf | |
| │ └── ... | |
| └── target_study/ | |
| ├── checklist.json | |
| ├── paper.pdf | |
| └── images/ | |
| ``` | |
| --- | |
| ## What The Space Checks | |
| - top-level folder structure and missing or extra files | |
| - `task_info.json` and `checklist.json` parseability and required keys | |
| - file naming conventions such as `related_work/paper_000.pdf` | |
| - whether declared data paths actually exist | |
| - whether image references actually exist | |
| - whether invalid source paths or stale `/tasks/...` references remain in descriptions | |
| """ | |
| def final_task_help_html() -> str: | |
| return ( | |
| '<div class="caption">' | |
| 'The final task ID is assigned automatically after the Space scans existing <code>tasks/</code> folders. ' | |
| 'You do not need to choose the numeric suffix yourself. The selected domain becomes the prefix, and if the ' | |
| 'custom field is filled, it overrides the suggested domain.' | |
| '</div>' | |
| ) | |
| def resolve_domain(selected_domain: str, custom_domain: str) -> str: | |
| raw_value = (custom_domain or '').strip() or (selected_domain or '').strip() | |
| normalized = normalize_domain_token(raw_value) | |
| if not normalized: | |
| raise ValidationError('Please select a suggested domain or provide a custom domain.') | |
| return normalized | |
| def handle_archive_upload(archive_path: str | None, current_archive_path: str | None): | |
| if current_archive_path and current_archive_path != archive_path: | |
| cleanup_uploaded_archive(current_archive_path) | |
| if not archive_path: | |
| return '', 'No ZIP file selected yet.' | |
| managed_archive_path = persist_uploaded_archive(archive_path) | |
| original_path = Path(archive_path) | |
| managed_name = managed_archive_path.name | |
| if managed_archive_path.resolve() != original_path.resolve(): | |
| try: | |
| original_path.unlink() | |
| except OSError: | |
| pass | |
| return str(managed_archive_path), f'Selected ZIP: `{managed_name}`' | |
| def archive_notice_text(archive_path: str | None) -> str: | |
| if not archive_path: | |
| return 'No ZIP file selected yet.' | |
| return f'Selected ZIP: `{Path(archive_path).name}`' | |
| def build_validation_markdown(prepared: PreparedSubmission) -> str: | |
| metadata = prepared.metadata | |
| return '\n'.join([ | |
| '## Validation passed', | |
| '', | |
| f'- Final task ID: `{prepared.assigned_task_id}`', | |
| '- This is the folder name that will be created under `tasks/` in the dataset repo.', | |
| f'- Domain token used for allocation: `{metadata.domain}`', | |
| f'- Submitter: `{metadata.submitter}`', | |
| f'- Archive file count: `{prepared.archive_stats.file_count}`', | |
| f'- Archive total bytes: `{prepared.archive_stats.total_bytes}`', | |
| '', | |
| 'You can now create a PR to the Hugging Face dataset repo.', | |
| ]) | |
| def build_failure_markdown(message: str) -> str: | |
| items = [line.strip() for line in message.splitlines() if line.strip()] | |
| bullets = '\n'.join(f'- {item}' for item in items) if items else '- Unknown validation error' | |
| return f'## Validation failed\n\n{bullets}' | |
| def refresh_prepared_submission_for_pr( | |
| prepared: PreparedSubmission, | |
| *, | |
| repo_id: str, | |
| token: str | None, | |
| ) -> tuple[PreparedSubmission, bool, str]: | |
| head_sha = get_repo_head_sha(repo_id=repo_id, token=token) | |
| existing_ids = list_existing_task_ids(repo_id=repo_id, token=token) | |
| reassigned = False | |
| final_task_id = prepared.assigned_task_id | |
| if final_task_id in existing_ids: | |
| final_task_id = allocate_next_task_id(prepared.metadata.domain, existing_ids) | |
| prepared.assigned_task_id = final_task_id | |
| prepared.staged_task_dir = str( | |
| stage_submission(prepared.uploaded_task_dir, final_task_id, prepared.work_dir) | |
| ) | |
| reassigned = True | |
| return prepared, reassigned, head_sha | |
| def is_retryable_pr_error(exc: Exception) -> bool: | |
| if not isinstance(exc, HfHubHTTPError): | |
| return False | |
| status_code = getattr(getattr(exc, 'response', None), 'status_code', None) | |
| message = str(exc).lower() | |
| return status_code in {409, 412} or 'parent commit' in message or 'conflict' in message or 'stale' in message | |
| def validate_submission( | |
| archive_path: str, | |
| suggested_domain: str, | |
| custom_domain: str, | |
| submitter: str, | |
| email: str, | |
| paper_title: str, | |
| paper_url: str, | |
| notes: str, | |
| current_state: dict | None, | |
| ): | |
| if current_state: | |
| cleanup_work_dir(current_state.get('work_dir')) | |
| if not archive_path: | |
| return ( | |
| None, | |
| '', | |
| '', | |
| '## Validation failed\n\n- Please upload a zip file.', | |
| '{}', | |
| gr.update(interactive=False), | |
| '', | |
| archive_notice_text(None), | |
| ) | |
| domain = resolve_domain(suggested_domain, custom_domain) | |
| token = load_hf_token() | |
| metadata = SubmissionMetadata( | |
| domain=domain, | |
| submitter=submitter, | |
| email=email, | |
| paper_title=paper_title, | |
| paper_url=paper_url, | |
| notes=notes or '', | |
| ) | |
| try: | |
| existing_ids = list_existing_task_ids(repo_id=DEFAULT_REPO_ID, token=token) | |
| assigned_task_id = allocate_next_task_id(domain, existing_ids) | |
| prepared = validate_and_prepare_submission(archive_path, metadata, assigned_task_id) | |
| pr_ready = bool(token) | |
| return ( | |
| prepared.to_state(), | |
| archive_path, | |
| prepared.assigned_task_id, | |
| build_validation_markdown(prepared), | |
| json.dumps(build_public_report(prepared), indent=2, ensure_ascii=False), | |
| gr.update(interactive=pr_ready), | |
| '' if pr_ready else 'Validation passed, but PR creation is disabled until a write token is configured.', | |
| archive_notice_text(archive_path), | |
| ) | |
| except ValidationError as exc: | |
| cleanup_uploaded_archive(archive_path) | |
| return ( | |
| None, | |
| '', | |
| '', | |
| build_failure_markdown(str(exc)), | |
| json.dumps({'status': 'error', 'errors': str(exc).splitlines()}, indent=2, ensure_ascii=False), | |
| gr.update(interactive=False), | |
| '', | |
| archive_notice_text(None), | |
| ) | |
| except Exception as exc: | |
| cleanup_uploaded_archive(archive_path) | |
| return ( | |
| None, | |
| '', | |
| '', | |
| build_failure_markdown(str(exc)), | |
| json.dumps({'status': 'error', 'errors': [str(exc)]}, indent=2, ensure_ascii=False), | |
| gr.update(interactive=False), | |
| '', | |
| archive_notice_text(None), | |
| ) | |
| def create_pr(state: dict | None, archive_path: str | None): | |
| if not state: | |
| return ( | |
| None, | |
| '', | |
| gr.update(interactive=False), | |
| '## PR creation failed\n\n- Validate a submission first.', | |
| 'No ZIP file selected yet.', | |
| ) | |
| prepared = PreparedSubmission.from_state(state) | |
| token = load_hf_token() | |
| reassigned = False | |
| for attempt in range(2): | |
| try: | |
| prepared, was_reassigned, head_sha = refresh_prepared_submission_for_pr( | |
| prepared, | |
| repo_id=DEFAULT_REPO_ID, | |
| token=token, | |
| ) | |
| reassigned = reassigned or was_reassigned | |
| commit_info = create_dataset_pr( | |
| prepared, | |
| repo_id=DEFAULT_REPO_ID, | |
| token=token, | |
| parent_commit=head_sha, | |
| ) | |
| pr_url = commit_info.pr_url or commit_info.commit_url | |
| lines = [ | |
| '## PR created', | |
| '', | |
| f'- Task ID: `{prepared.assigned_task_id}`', | |
| f'- PR: {pr_url}', | |
| ] | |
| if reassigned: | |
| lines.insert(3, '- The task ID was reassigned at PR time because the previously validated ID is no longer available on the dataset main branch.') | |
| message = '\n'.join(lines) | |
| cleanup_work_dir(prepared.work_dir) | |
| cleanup_uploaded_archive(archive_path) | |
| return None, '', gr.update(interactive=False), message, archive_notice_text(None) | |
| except Exception as exc: | |
| if attempt == 0 and is_retryable_pr_error(exc): | |
| continue | |
| message = str(exc).strip() or 'Unknown PR creation error' | |
| if is_retryable_pr_error(exc): | |
| message += '\nPlease click "Create Dataset PR" again. The dataset main branch changed while your PR was being created.' | |
| return ( | |
| prepared.to_state(), | |
| archive_path or '', | |
| gr.update(interactive=bool(token)), | |
| build_failure_markdown(message), | |
| archive_notice_text(archive_path), | |
| ) | |
| with gr.Blocks(title=SPACE_TITLE, fill_width=True) as demo: | |
| state = gr.State(None, time_to_live=STATE_TTL_SECONDS, delete_callback=cleanup_submission_state) | |
| archive_state = gr.State('', time_to_live=STATE_TTL_SECONDS, delete_callback=cleanup_uploaded_archive) | |
| gr.HTML(build_hero_html()) | |
| with gr.Group(elem_classes=['page-shell']): | |
| with gr.Row(): | |
| with gr.Column(scale=1, min_width=0, elem_classes=['shell-spacer']): | |
| gr.HTML('') | |
| with gr.Column(scale=30, min_width=0, elem_classes=['page-shell-content']): | |
| with gr.Row(elem_classes=['section-row']): | |
| with gr.Column(scale=7, elem_classes=['section-copy', 'main-form']): | |
| gr.HTML(field_label_html('Task ZIP archive')) | |
| with gr.Row(elem_classes=['upload-row']): | |
| archive = gr.UploadButton( | |
| 'Select ZIP file', | |
| file_types=['.zip'], | |
| file_count='single', | |
| type='filepath', | |
| variant='secondary', | |
| elem_classes=['upload-button'], | |
| ) | |
| archive_notice = gr.Markdown('No ZIP file selected yet.', elem_classes=['upload-status']) | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.HTML(field_label_html('Suggested domain')) | |
| suggested_domain = gr.Dropdown( | |
| choices=list(DOMAINS), | |
| value='Astronomy', | |
| show_label=False, | |
| container=False, | |
| ) | |
| with gr.Column(): | |
| gr.HTML(field_label_html('Custom domain (optional)')) | |
| custom_domain = gr.Textbox( | |
| placeholder='e.g. Robotics or Robot-Learning', | |
| show_label=False, | |
| container=False, | |
| ) | |
| gr.Markdown( | |
| '<div class="caption">Use the custom field if your task does not belong to the suggested list. ' | |
| 'If the custom field is filled, it overrides the suggested domain and becomes the prefix of the final task ID.</div>' | |
| ) | |
| gr.HTML(field_label_html('Submitter name or HF username')) | |
| submitter = gr.Textbox( | |
| placeholder='e.g. your-hf-handle', | |
| show_label=False, | |
| container=False, | |
| ) | |
| gr.HTML(field_label_html('Contact email')) | |
| email = gr.Textbox( | |
| placeholder='name@example.com', | |
| show_label=False, | |
| container=False, | |
| ) | |
| gr.HTML(field_label_html('Target paper title')) | |
| paper_title = gr.Textbox(show_label=False, container=False) | |
| gr.HTML(field_label_html('Target paper URL or DOI')) | |
| paper_url = gr.Textbox( | |
| placeholder='https://... or DOI', | |
| show_label=False, | |
| container=False, | |
| ) | |
| gr.HTML(field_label_html('Optional notes for reviewers')) | |
| notes = gr.Textbox( | |
| lines=4, | |
| placeholder='Anything maintainers should know about licensing, preprocessing, or provenance.', | |
| show_label=False, | |
| container=False, | |
| ) | |
| with gr.Column(scale=5, elem_classes=['section-copy', 'side-notes']): | |
| gr.Markdown(submission_guide_markdown(), elem_classes=['subtle-block']) | |
| with gr.Row(elem_classes=['action-row']): | |
| validate_btn = gr.Button('Validate ZIP', variant='primary', elem_classes=['primary-button']) | |
| create_pr_btn = gr.Button('Create Dataset PR', interactive=False, elem_classes=['secondary-button']) | |
| with gr.Column(elem_classes=['section-copy', 'results-shell']): | |
| gr.HTML(field_label_html('Final task ID (assigned automatically)')) | |
| assigned_task_id = gr.Textbox( | |
| interactive=False, | |
| show_label=False, | |
| container=False, | |
| ) | |
| gr.Markdown(final_task_help_html()) | |
| validation_md = gr.Markdown() | |
| gr.HTML(field_label_html('Validation report')) | |
| validation_report = gr.Code(language='json', show_label=False, container=False) | |
| pr_md = gr.Markdown() | |
| with gr.Column(scale=1, min_width=0, elem_classes=['shell-spacer']): | |
| gr.HTML('') | |
| archive.upload( | |
| fn=handle_archive_upload, | |
| inputs=[archive, archive_state], | |
| outputs=[archive_state, archive_notice], | |
| ) | |
| validate_btn.click( | |
| fn=validate_submission, | |
| inputs=[ | |
| archive_state, | |
| suggested_domain, | |
| custom_domain, | |
| submitter, | |
| email, | |
| paper_title, | |
| paper_url, | |
| notes, | |
| state, | |
| ], | |
| outputs=[ | |
| state, | |
| archive_state, | |
| assigned_task_id, | |
| validation_md, | |
| validation_report, | |
| create_pr_btn, | |
| pr_md, | |
| archive_notice, | |
| ], | |
| ) | |
| create_pr_btn.click( | |
| fn=create_pr, | |
| inputs=[ | |
| state, | |
| archive_state, | |
| ], | |
| outputs=[ | |
| state, | |
| archive_state, | |
| create_pr_btn, | |
| pr_md, | |
| archive_notice, | |
| ], | |
| ) | |
| if __name__ == '__main__': | |
| demo.launch( | |
| theme=gr.themes.Base(), | |
| css=CSS, | |
| ssr_mode=False, | |
| server_name=os.environ.get('GRADIO_SERVER_NAME', '0.0.0.0'), | |
| server_port=int(os.environ.get('GRADIO_SERVER_PORT', os.environ.get('PORT', '7860'))), | |
| ) | |