|
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() |
|
|