| import io |
| import json |
|
|
| import gradio as gr |
| from huggingface_hub import HfApi |
| from loguru import logger |
|
|
| from competitions.utils import token_information |
|
|
|
|
| COMPETITION_DESC = """Sample competition description""" |
| DATASET_DESC = """Sample dataset description""" |
| SUBMISSION_DESC = """Sample submission description""" |
| RULES = """Sample rules""" |
| SOLUTION_CSV = """ |
| id,pred,split |
| 0,1,public |
| 1,0,private |
| 2,0,private |
| 3,1,private |
| 4,0,public |
| 5,1,private |
| 6,1,public |
| 7,1,private |
| 8,0,public |
| 9,0,private |
| 10,0,private |
| 11,0,private |
| 12,1,private |
| 13,0,private |
| 14,1,public |
| 15,1,private |
| 16,1,private |
| 17,0,private |
| 18,0,private |
| 19,0,public |
| 20,0,private |
| 21,0,private |
| 22,1,private |
| 23,1,public |
| 24,0,private |
| 25,0,private |
| 26,0,public |
| 27,1,private |
| 28,1,private |
| 29,0,private |
| 30,0,public |
| """ |
| SOLUTION_CSV = SOLUTION_CSV.strip() |
|
|
| DOCKERFILE = """ |
| FROM huggingface/competitions:latest |
| |
| CMD uvicorn competitions.app:app --host 0.0.0.0 --port 7860 --workers 1 |
| """ |
| DOCKERFILE = DOCKERFILE.replace("\n", " ").replace(" ", "\n").strip() |
|
|
| HARDWARE_CHOICES = [ |
| "cpu-basic", |
| "cpu-upgrade", |
| "t4-small", |
| "t4-medium", |
| "a10g-small", |
| "a10g-large", |
| "a10g-largex2", |
| "a10g-largex4", |
| "a100-large", |
| ] |
| METRIC_CHOICES = [ |
| "accuracy_score", |
| "f1_score", |
| "hamming_loss", |
| "jaccard_score", |
| "log_loss", |
| "roc_auc_score", |
| "mean_squared_error", |
| "mean_absolute_error", |
| "r2_score", |
| "custom", |
| ] |
|
|
|
|
| def check_if_user_can_create_competition(user_token): |
| """ |
| Check if the user can create a competition |
| :param user_token: the user's token |
| :return: True if the user can create a competition, False otherwise |
| """ |
| user_info = token_information(user_token) |
| valid_orgs = user_info["orgs"] |
|
|
| return gr.Dropdown( |
| choices=valid_orgs, |
| visible=True, |
| value=valid_orgs[0], |
| ) |
|
|
|
|
| def _create_readme(competition_name): |
| _readme = "---\n" |
| _readme += f"title: {competition_name}\n" |
| _readme += "emoji: 🚀\n" |
| _readme += "colorFrom: green\n" |
| _readme += "colorTo: indigo\n" |
| _readme += "sdk: docker\n" |
| _readme += "pinned: false\n" |
| _readme += "tags:\n" |
| _readme += " - competition\n" |
| _readme += "hf_oauth: true\n" |
| _readme += "hf_oauth_scopes:\n" |
| _readme += " - read-repos\n" |
| _readme += "---\n" |
| _readme = io.BytesIO(_readme.encode()) |
| return _readme |
|
|
|
|
| def _create( |
| user_token, |
| organization, |
| competition_name, |
| competition_logo, |
| hardware, |
| competition_type, |
| time_limit, |
| metric, |
| metric_higher_is_better, |
| submission_limit, |
| selection_limit, |
| end_date, |
| submission_id_column, |
| submission_columns, |
| submission_rows, |
| ): |
| """ |
| Create a competition |
| """ |
|
|
| |
| competition_name = "".join([c for c in competition_name if c.isalnum()]) |
| if len(competition_name) == 0: |
| raise gr.Error("Please provide a valid alphanumeric competition name") |
|
|
| conf_json = { |
| "COMPETITION_TYPE": competition_type, |
| "SUBMISSION_LIMIT": int(submission_limit), |
| "TIME_LIMIT": int(time_limit), |
| "SELECTION_LIMIT": int(selection_limit), |
| "HARDWARE": hardware, |
| "END_DATE": end_date, |
| "EVAL_HIGHER_IS_BETTER": metric_higher_is_better is True, |
| "SUBMISSION_ID_COLUMN": submission_id_column, |
| "SUBMISSION_COLUMNS": submission_columns, |
| "SUBMISSION_ROWS": int(submission_rows), |
| "EVAL_METRIC": metric, |
| "LOGO": competition_logo, |
| "DATASET": "", |
| "SUBMISSION_FILENAMES": ["submission.csv"], |
| "SCORING_METRIC": "", |
| } |
| teams_json = {} |
| user_team_json = {} |
|
|
| logger.info(f"Creating competition: {competition_name}") |
|
|
| api = HfApi(token=user_token) |
| api.create_repo( |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| private=True, |
| ) |
|
|
| conf_json = json.dumps(conf_json, indent=4) |
| conf_json_bytes = conf_json.encode("utf-8") |
| conf_json_buffer = io.BytesIO(conf_json_bytes) |
| api.upload_file( |
| path_or_fileobj=conf_json_buffer, |
| path_in_repo="conf.json", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| ) |
|
|
| teams_json = json.dumps(teams_json, indent=4) |
| teams_json_bytes = teams_json.encode("utf-8") |
| teams_json_buffer = io.BytesIO(teams_json_bytes) |
| api.upload_file( |
| path_or_fileobj=teams_json_buffer, |
| path_in_repo="teams.json", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| ) |
|
|
| user_team_json = json.dumps(user_team_json, indent=4) |
| user_team_json_bytes = user_team_json.encode("utf-8") |
| user_team_json_buffer = io.BytesIO(user_team_json_bytes) |
| api.upload_file( |
| path_or_fileobj=user_team_json_buffer, |
| path_in_repo="user_team.json", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| ) |
|
|
| comp_desc = io.BytesIO(COMPETITION_DESC.encode()) |
| api.upload_file( |
| path_or_fileobj=comp_desc, |
| path_in_repo="COMPETITION_DESC.md", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| ) |
|
|
| dataset_desc = io.BytesIO(DATASET_DESC.encode()) |
| api.upload_file( |
| path_or_fileobj=dataset_desc, |
| path_in_repo="DATASET_DESC.md", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| ) |
|
|
| submission_desc = io.BytesIO(SUBMISSION_DESC.encode()) |
| api.upload_file( |
| path_or_fileobj=submission_desc, |
| path_in_repo="SUBMISSION_DESC.md", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| ) |
|
|
| solution_csv = io.BytesIO(SOLUTION_CSV.encode()) |
| api.upload_file( |
| path_or_fileobj=solution_csv, |
| path_in_repo="solution.csv", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| ) |
|
|
| rules = io.BytesIO(RULES.encode()) |
| api.upload_file( |
| path_or_fileobj=rules, |
| path_in_repo="RULES.md", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="dataset", |
| ) |
|
|
| |
| api.create_repo( |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="space", |
| space_sdk="docker", |
| space_hardware="cpu-basic" if competition_type == "script" else hardware, |
| private=True, |
| ) |
| api.add_space_secret(repo_id=f"{organization}/{competition_name}", key="HF_TOKEN", value=user_token) |
| api.add_space_secret( |
| repo_id=f"{organization}/{competition_name}", |
| key="COMPETITION_ID", |
| value=f"{organization}/{competition_name}", |
| ) |
| readme = _create_readme(competition_name) |
| api.upload_file( |
| path_or_fileobj=readme, |
| path_in_repo="README.md", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="space", |
| ) |
|
|
| _dockerfile = io.BytesIO(DOCKERFILE.encode()) |
| api.upload_file( |
| path_or_fileobj=_dockerfile, |
| path_in_repo="Dockerfile", |
| repo_id=f"{organization}/{competition_name}", |
| repo_type="space", |
| ) |
|
|
| return gr.Markdown( |
| value=f"""Created private dataset and competition space. |
| To make competition public, you should make the space public. |
| Please note that the dataset should always be kept private. |
| |
| Private dataset: https://huggingface.co/datasets/{organization}/{competition_name} |
| |
| Competition space: https://huggingface.co/spaces/{organization}/{competition_name} |
| |
| Note: there's still some work left. Now you must change the solution.csv file to your own solution, |
| and make changes to *_desc.md files to reflect your competition. You may also change conf.json |
| to suit your needs. Please refer to the [documentation](https://hf.co/docs/competitions) for more information. |
| """ |
| ) |
|
|
|
|
| def main(): |
| with gr.Blocks() as demo: |
| gr.Markdown("# Hugging Face Competition Creator") |
| token = gr.Textbox(label="Your Hugging Face write token", lines=1, type="password") |
| with gr.Row(): |
| organization = gr.Dropdown(label="Organization name", choices=[""]) |
| competition_name = gr.Textbox(label="Competition name", lines=1) |
| competition_logo = gr.Textbox(label="Competition logo", value="https://mysite.com/mylogo.png", lines=1) |
| with gr.Group(): |
| with gr.Row(): |
| hardware = gr.Dropdown(label="Hardware to use", choices=HARDWARE_CHOICES, value=HARDWARE_CHOICES[0]) |
| competition_type = gr.Dropdown( |
| label="Competition type", choices=["generic", "script"], value="generic" |
| ) |
| time_limit = gr.Textbox( |
| label="Time limit (s). Only used for script competitions", lines=1, value="3600" |
| ) |
| with gr.Row(): |
| metric = gr.Dropdown(label="Metric to use", choices=METRIC_CHOICES, value=METRIC_CHOICES[0]) |
| metric_higher_is_better = gr.Dropdown( |
| label="Is higher metric better?", choices=[True, False], value=True |
| ) |
| with gr.Row(): |
| submission_limit = gr.Textbox(label="Submission limit per day", lines=1, value="5") |
| selection_limit = gr.Textbox(label="Final selection limit", lines=1, value="2") |
| end_date = gr.Textbox(label="End date (YYYY-MM-DD)", lines=1, value="2024-12-31") |
| with gr.Row(): |
| submission_id_column = gr.Textbox(label="Submission id column", lines=1, value="id") |
| submission_columns = gr.Textbox(label="Submission columns", lines=1, value="id,pred") |
| submission_rows = gr.Textbox(label="Submission total rows (exclusing header)", lines=1, value="10000") |
|
|
| output_md = gr.Markdown("Click the button below to create the competition") |
| create_competition = gr.Button(value="Create competition") |
| token.change(check_if_user_can_create_competition, inputs=token, outputs=organization) |
|
|
| create_competition.click( |
| _create, |
| inputs=[ |
| token, |
| organization, |
| competition_name, |
| competition_logo, |
| hardware, |
| competition_type, |
| time_limit, |
| metric, |
| metric_higher_is_better, |
| submission_limit, |
| selection_limit, |
| end_date, |
| submission_id_column, |
| submission_columns, |
| submission_rows, |
| ], |
| outputs=output_md, |
| ) |
| return demo |
|
|
|
|
| if __name__ == "__main__": |
| main().launch() |
|
|