Commit
·
90b78ed
1
Parent(s):
447fff1
update features for hugsim competition
Browse files- competitions/app.py +57 -19
- competitions/runner.py +27 -8
- competitions/static/Competition Participants Information Collection Template.xlsx +0 -0
- competitions/submissions.py +47 -115
- competitions/templates/index.html +139 -16
- competitions/utils.py +196 -59
competitions/app.py
CHANGED
|
@@ -2,9 +2,11 @@ import datetime
|
|
| 2 |
import os
|
| 3 |
import threading
|
| 4 |
import time
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
from fastapi import Depends, FastAPI, File, Form, HTTPException, Request, UploadFile
|
| 7 |
-
from fastapi.responses import HTMLResponse, JSONResponse
|
| 8 |
from fastapi.staticfiles import StaticFiles
|
| 9 |
from fastapi.templating import Jinja2Templates
|
| 10 |
from huggingface_hub import hf_hub_download
|
|
@@ -101,7 +103,7 @@ templates = Jinja2Templates(directory=templates_path)
|
|
| 101 |
|
| 102 |
|
| 103 |
@app.get("/", response_class=HTMLResponse)
|
| 104 |
-
|
| 105 |
"""
|
| 106 |
This function is used to render the HTML file
|
| 107 |
:param request:
|
|
@@ -121,14 +123,23 @@ async def read_form(request: Request):
|
|
| 121 |
|
| 122 |
|
| 123 |
@app.get("/login_status", response_class=JSONResponse)
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
if user_token:
|
| 126 |
-
|
| 127 |
-
|
|
|
|
|
|
|
| 128 |
|
| 129 |
|
| 130 |
@app.get("/logout", response_class=HTMLResponse)
|
| 131 |
-
|
| 132 |
"""Endpoint that logs out the user (e.g. delete cookie session)."""
|
| 133 |
|
| 134 |
if "oauth_info" in request.session:
|
|
@@ -147,7 +158,7 @@ async def user_logout(request: Request):
|
|
| 147 |
|
| 148 |
|
| 149 |
@app.get("/competition_info", response_class=JSONResponse)
|
| 150 |
-
|
| 151 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 152 |
info = competition_info.competition_desc
|
| 153 |
resp = {"response": info}
|
|
@@ -155,7 +166,7 @@ async def get_comp_info(request: Request):
|
|
| 155 |
|
| 156 |
|
| 157 |
@app.get("/dataset_info", response_class=JSONResponse)
|
| 158 |
-
|
| 159 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 160 |
info = competition_info.dataset_desc
|
| 161 |
resp = {"response": info}
|
|
@@ -163,7 +174,7 @@ async def get_dataset_info(request: Request):
|
|
| 163 |
|
| 164 |
|
| 165 |
@app.get("/rules", response_class=JSONResponse)
|
| 166 |
-
|
| 167 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 168 |
if competition_info.rules is not None:
|
| 169 |
return {"response": competition_info.rules}
|
|
@@ -171,7 +182,7 @@ async def get_rules(request: Request):
|
|
| 171 |
|
| 172 |
|
| 173 |
@app.get("/submission_info", response_class=JSONResponse)
|
| 174 |
-
|
| 175 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 176 |
info = competition_info.submission_desc
|
| 177 |
resp = {"response": info}
|
|
@@ -179,7 +190,7 @@ async def get_submission_info(request: Request):
|
|
| 179 |
|
| 180 |
|
| 181 |
@app.post("/leaderboard", response_class=JSONResponse)
|
| 182 |
-
|
| 183 |
request: Request, body: LeaderboardRequest, user_token: str = Depends(utils.user_authentication)
|
| 184 |
):
|
| 185 |
lb = body.lb
|
|
@@ -215,7 +226,7 @@ async def fetch_leaderboard(
|
|
| 215 |
|
| 216 |
|
| 217 |
@app.post("/my_submissions", response_class=JSONResponse)
|
| 218 |
-
|
| 219 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 220 |
if user_token is None:
|
| 221 |
return {
|
|
@@ -253,7 +264,7 @@ async def my_submissions(request: Request, user_token: str = Depends(utils.user_
|
|
| 253 |
submission_text = SUBMISSION_TEXT.format(competition_info.submission_limit)
|
| 254 |
submission_selection_text = SUBMISSION_SELECTION_TEXT.format(competition_info.selection_limit)
|
| 255 |
|
| 256 |
-
team_name = utils.
|
| 257 |
|
| 258 |
resp = {
|
| 259 |
"response": {
|
|
@@ -267,7 +278,7 @@ async def my_submissions(request: Request, user_token: str = Depends(utils.user_
|
|
| 267 |
|
| 268 |
|
| 269 |
@app.post("/new_submission", response_class=JSONResponse)
|
| 270 |
-
|
| 271 |
request: Request,
|
| 272 |
submission_file: UploadFile = File(None),
|
| 273 |
hub_model: str = Form(...),
|
|
@@ -358,14 +369,14 @@ def update_team_name(
|
|
| 358 |
return {"success": False, "error": "Team name cannot be empty."}
|
| 359 |
|
| 360 |
try:
|
| 361 |
-
utils.
|
| 362 |
return {"success": True, "error": ""}
|
| 363 |
except Exception as e:
|
| 364 |
return {"success": False, "error": str(e)}
|
| 365 |
|
| 366 |
|
| 367 |
@app.post("/admin/comp_info", response_class=JSONResponse)
|
| 368 |
-
|
| 369 |
comp_org = COMPETITION_ID.split("/")[0]
|
| 370 |
user_is_admin = utils.is_user_admin(user_token, comp_org)
|
| 371 |
if not user_is_admin:
|
|
@@ -404,7 +415,7 @@ async def admin_comp_info(request: Request, user_token: str = Depends(utils.user
|
|
| 404 |
|
| 405 |
|
| 406 |
@app.post("/admin/update_comp_info", response_class=JSONResponse)
|
| 407 |
-
|
| 408 |
comp_org = COMPETITION_ID.split("/")[0]
|
| 409 |
user_is_admin = utils.is_user_admin(user_token, comp_org)
|
| 410 |
if not user_is_admin:
|
|
@@ -412,7 +423,6 @@ async def update_comp_info(request: Request, user_token: str = Depends(utils.use
|
|
| 412 |
|
| 413 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 414 |
|
| 415 |
-
data = await request.json()
|
| 416 |
config = data["config"]
|
| 417 |
markdowns = data["markdowns"]
|
| 418 |
|
|
@@ -445,3 +455,31 @@ async def update_comp_info(request: Request, user_token: str = Depends(utils.use
|
|
| 445 |
return {"success": False}, 500
|
| 446 |
|
| 447 |
return {"success": True}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import os
|
| 3 |
import threading
|
| 4 |
import time
|
| 5 |
+
import io
|
| 6 |
+
from typing import Dict, Any
|
| 7 |
|
| 8 |
+
from fastapi import Depends, FastAPI, File, Form, HTTPException, Request, UploadFile, Body
|
| 9 |
+
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
|
| 10 |
from fastapi.staticfiles import StaticFiles
|
| 11 |
from fastapi.templating import Jinja2Templates
|
| 12 |
from huggingface_hub import hf_hub_download
|
|
|
|
| 103 |
|
| 104 |
|
| 105 |
@app.get("/", response_class=HTMLResponse)
|
| 106 |
+
def read_form(request: Request):
|
| 107 |
"""
|
| 108 |
This function is used to render the HTML file
|
| 109 |
:param request:
|
|
|
|
| 123 |
|
| 124 |
|
| 125 |
@app.get("/login_status", response_class=JSONResponse)
|
| 126 |
+
def use_oauth(request: Request, user_token: str = Depends(utils.user_authentication)):
|
| 127 |
+
result = {
|
| 128 |
+
"is_login": False,
|
| 129 |
+
"is_admin": False,
|
| 130 |
+
"is_registered": False,
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
comp_org = COMPETITION_ID.split("/")[0]
|
| 134 |
if user_token:
|
| 135 |
+
result["is_login"] = True
|
| 136 |
+
result["is_admin"] = utils.is_user_admin(user_token, comp_org)
|
| 137 |
+
result["is_registered"] = utils.team_file_api.get_team_info(user_token) is not None
|
| 138 |
+
return {"response": result}
|
| 139 |
|
| 140 |
|
| 141 |
@app.get("/logout", response_class=HTMLResponse)
|
| 142 |
+
def user_logout(request: Request):
|
| 143 |
"""Endpoint that logs out the user (e.g. delete cookie session)."""
|
| 144 |
|
| 145 |
if "oauth_info" in request.session:
|
|
|
|
| 158 |
|
| 159 |
|
| 160 |
@app.get("/competition_info", response_class=JSONResponse)
|
| 161 |
+
def get_comp_info(request: Request):
|
| 162 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 163 |
info = competition_info.competition_desc
|
| 164 |
resp = {"response": info}
|
|
|
|
| 166 |
|
| 167 |
|
| 168 |
@app.get("/dataset_info", response_class=JSONResponse)
|
| 169 |
+
def get_dataset_info(request: Request):
|
| 170 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 171 |
info = competition_info.dataset_desc
|
| 172 |
resp = {"response": info}
|
|
|
|
| 174 |
|
| 175 |
|
| 176 |
@app.get("/rules", response_class=JSONResponse)
|
| 177 |
+
def get_rules(request: Request):
|
| 178 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 179 |
if competition_info.rules is not None:
|
| 180 |
return {"response": competition_info.rules}
|
|
|
|
| 182 |
|
| 183 |
|
| 184 |
@app.get("/submission_info", response_class=JSONResponse)
|
| 185 |
+
def get_submission_info(request: Request):
|
| 186 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 187 |
info = competition_info.submission_desc
|
| 188 |
resp = {"response": info}
|
|
|
|
| 190 |
|
| 191 |
|
| 192 |
@app.post("/leaderboard", response_class=JSONResponse)
|
| 193 |
+
def fetch_leaderboard(
|
| 194 |
request: Request, body: LeaderboardRequest, user_token: str = Depends(utils.user_authentication)
|
| 195 |
):
|
| 196 |
lb = body.lb
|
|
|
|
| 226 |
|
| 227 |
|
| 228 |
@app.post("/my_submissions", response_class=JSONResponse)
|
| 229 |
+
def my_submissions(request: Request, user_token: str = Depends(utils.user_authentication)):
|
| 230 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 231 |
if user_token is None:
|
| 232 |
return {
|
|
|
|
| 264 |
submission_text = SUBMISSION_TEXT.format(competition_info.submission_limit)
|
| 265 |
submission_selection_text = SUBMISSION_SELECTION_TEXT.format(competition_info.selection_limit)
|
| 266 |
|
| 267 |
+
team_name = utils.team_file_api.get_team_info(user_token)["name"]
|
| 268 |
|
| 269 |
resp = {
|
| 270 |
"response": {
|
|
|
|
| 278 |
|
| 279 |
|
| 280 |
@app.post("/new_submission", response_class=JSONResponse)
|
| 281 |
+
def new_submission(
|
| 282 |
request: Request,
|
| 283 |
submission_file: UploadFile = File(None),
|
| 284 |
hub_model: str = Form(...),
|
|
|
|
| 369 |
return {"success": False, "error": "Team name cannot be empty."}
|
| 370 |
|
| 371 |
try:
|
| 372 |
+
utils.team_file_api.update_and_create_team_name(user_token, new_team_name)
|
| 373 |
return {"success": True, "error": ""}
|
| 374 |
except Exception as e:
|
| 375 |
return {"success": False, "error": str(e)}
|
| 376 |
|
| 377 |
|
| 378 |
@app.post("/admin/comp_info", response_class=JSONResponse)
|
| 379 |
+
def admin_comp_info(request: Request, user_token: str = Depends(utils.user_authentication)):
|
| 380 |
comp_org = COMPETITION_ID.split("/")[0]
|
| 381 |
user_is_admin = utils.is_user_admin(user_token, comp_org)
|
| 382 |
if not user_is_admin:
|
|
|
|
| 415 |
|
| 416 |
|
| 417 |
@app.post("/admin/update_comp_info", response_class=JSONResponse)
|
| 418 |
+
def update_comp_info(data: Dict[str, Any] = Body(...), user_token: str = Depends(utils.user_authentication)):
|
| 419 |
comp_org = COMPETITION_ID.split("/")[0]
|
| 420 |
user_is_admin = utils.is_user_admin(user_token, comp_org)
|
| 421 |
if not user_is_admin:
|
|
|
|
| 423 |
|
| 424 |
competition_info = CompetitionInfo(competition_id=COMPETITION_ID, autotrain_token=HF_TOKEN)
|
| 425 |
|
|
|
|
| 426 |
config = data["config"]
|
| 427 |
markdowns = data["markdowns"]
|
| 428 |
|
|
|
|
| 455 |
return {"success": False}, 500
|
| 456 |
|
| 457 |
return {"success": True}
|
| 458 |
+
|
| 459 |
+
|
| 460 |
+
@app.get("/register_template_file")
|
| 461 |
+
def register_example_file():
|
| 462 |
+
file_path = os.path.join(BASE_DIR, "static", "Competition Participants Information Collection Template.xlsx")
|
| 463 |
+
return FileResponse(file_path, media_type="application/octet-stream", filename="Competition Participants Information Collection Template.xlsx")
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
@app.post("/register")
|
| 467 |
+
def register(
|
| 468 |
+
teamname: str = Form(...),
|
| 469 |
+
file: UploadFile = Form(...),
|
| 470 |
+
user_token: str = Depends(utils.user_authentication)
|
| 471 |
+
):
|
| 472 |
+
if user_token is None:
|
| 473 |
+
return {"success": False, "response": "Please login."}
|
| 474 |
+
|
| 475 |
+
file_bytes = file.file.read()
|
| 476 |
+
file_stream = io.BytesIO(file_bytes)
|
| 477 |
+
file_stream.seek(0)
|
| 478 |
+
|
| 479 |
+
utils.team_file_api.create_team(
|
| 480 |
+
user_token,
|
| 481 |
+
teamname,
|
| 482 |
+
file_stream
|
| 483 |
+
)
|
| 484 |
+
|
| 485 |
+
return {"success": True, "response": "Team created successfully."}
|
competitions/runner.py
CHANGED
|
@@ -3,6 +3,7 @@ import io
|
|
| 3 |
import json
|
| 4 |
import os
|
| 5 |
import time
|
|
|
|
| 6 |
from dataclasses import dataclass
|
| 7 |
|
| 8 |
import pandas as pd
|
|
@@ -11,7 +12,7 @@ from loguru import logger
|
|
| 11 |
|
| 12 |
from competitions.enums import SubmissionStatus
|
| 13 |
from competitions.info import CompetitionInfo
|
| 14 |
-
from competitions.utils import run_evaluation
|
| 15 |
|
| 16 |
|
| 17 |
_DOCKERFILE = """
|
|
@@ -156,12 +157,16 @@ class JobRunner:
|
|
| 156 |
_readme += "colorTo: indigo\n"
|
| 157 |
_readme += "sdk: docker\n"
|
| 158 |
_readme += "pinned: false\n"
|
| 159 |
-
_readme += "duplicated_from: autotrain-projects/autotrain-advanced\n"
|
| 160 |
_readme += "---\n"
|
| 161 |
_readme = io.BytesIO(_readme.encode())
|
| 162 |
return _readme
|
| 163 |
|
| 164 |
def create_space(self, team_id, submission_id, submission_repo, space_id):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
api = HfApi(token=self.token)
|
| 166 |
params = {
|
| 167 |
"competition_id": self.competition_id,
|
|
@@ -180,23 +185,37 @@ class JobRunner:
|
|
| 180 |
"submission_filenames": self.submission_filenames,
|
| 181 |
}
|
| 182 |
|
| 183 |
-
api.add_space_secret(repo_id=
|
| 184 |
-
|
|
|
|
| 185 |
readme = self._create_readme(space_id.split("/")[-1])
|
| 186 |
api.upload_file(
|
| 187 |
path_or_fileobj=readme,
|
| 188 |
path_in_repo="README.md",
|
| 189 |
-
repo_id=
|
| 190 |
repo_type="space",
|
| 191 |
)
|
| 192 |
-
|
| 193 |
-
_dockerfile = io.BytesIO(_DOCKERFILE.encode())
|
| 194 |
api.upload_file(
|
| 195 |
-
path_or_fileobj=
|
| 196 |
path_in_repo="Dockerfile",
|
| 197 |
repo_id=space_id,
|
| 198 |
repo_type="space",
|
| 199 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
self._queue_submission(team_id, submission_id)
|
| 201 |
|
| 202 |
def run(self):
|
|
|
|
| 3 |
import json
|
| 4 |
import os
|
| 5 |
import time
|
| 6 |
+
import uuid
|
| 7 |
from dataclasses import dataclass
|
| 8 |
|
| 9 |
import pandas as pd
|
|
|
|
| 12 |
|
| 13 |
from competitions.enums import SubmissionStatus
|
| 14 |
from competitions.info import CompetitionInfo
|
| 15 |
+
from competitions.utils import run_evaluation, user_token_api
|
| 16 |
|
| 17 |
|
| 18 |
_DOCKERFILE = """
|
|
|
|
| 157 |
_readme += "colorTo: indigo\n"
|
| 158 |
_readme += "sdk: docker\n"
|
| 159 |
_readme += "pinned: false\n"
|
|
|
|
| 160 |
_readme += "---\n"
|
| 161 |
_readme = io.BytesIO(_readme.encode())
|
| 162 |
return _readme
|
| 163 |
|
| 164 |
def create_space(self, team_id, submission_id, submission_repo, space_id):
|
| 165 |
+
server_space_id = space_id + "-server"
|
| 166 |
+
client_space_id = space_id + "-client"
|
| 167 |
+
space_auth_token = uuid.uuid4().hex
|
| 168 |
+
user_token = user_token_api.get(team_id)
|
| 169 |
+
|
| 170 |
api = HfApi(token=self.token)
|
| 171 |
params = {
|
| 172 |
"competition_id": self.competition_id,
|
|
|
|
| 185 |
"submission_filenames": self.submission_filenames,
|
| 186 |
}
|
| 187 |
|
| 188 |
+
api.add_space_secret(repo_id=server_space_id, key="PARAMS", value=json.dumps(params))
|
| 189 |
+
api.add_space_secret(repo_id=server_space_id, key="HUGSIM_AUTH_TOKEN", value=space_auth_token)
|
| 190 |
+
api.add_space_secret(repo_id=server_space_id, key="HF_TOKEN", value=self.token)
|
| 191 |
readme = self._create_readme(space_id.split("/")[-1])
|
| 192 |
api.upload_file(
|
| 193 |
path_or_fileobj=readme,
|
| 194 |
path_in_repo="README.md",
|
| 195 |
+
repo_id=server_space_id,
|
| 196 |
repo_type="space",
|
| 197 |
)
|
|
|
|
|
|
|
| 198 |
api.upload_file(
|
| 199 |
+
path_or_fileobj=io.BytesIO(_DOCKERFILE.encode()),
|
| 200 |
path_in_repo="Dockerfile",
|
| 201 |
repo_id=space_id,
|
| 202 |
repo_type="space",
|
| 203 |
)
|
| 204 |
+
|
| 205 |
+
api.add_space_secret(repo_id=client_space_id, key="HUGSIM_AUTH_TOKEN", value=space_auth_token)
|
| 206 |
+
api.snapshot_download(
|
| 207 |
+
repo_id=submission_repo,
|
| 208 |
+
repo_type="model",
|
| 209 |
+
revision="main",
|
| 210 |
+
token=user_token,
|
| 211 |
+
local_dir="/tmp/data/user_repo",
|
| 212 |
+
allow_patterns=["*"],
|
| 213 |
+
)
|
| 214 |
+
api.upload_folder(
|
| 215 |
+
repo_id=client_space_id,
|
| 216 |
+
repo_type="space",
|
| 217 |
+
folder_path="/tmp/data/user_repo",
|
| 218 |
+
)
|
| 219 |
self._queue_submission(team_id, submission_id)
|
| 220 |
|
| 221 |
def run(self):
|
competitions/static/Competition Participants Information Collection Template.xlsx
ADDED
|
Binary file (48.2 kB). View file
|
|
|
competitions/submissions.py
CHANGED
|
@@ -6,10 +6,11 @@ from datetime import datetime
|
|
| 6 |
|
| 7 |
import pandas as pd
|
| 8 |
from huggingface_hub import HfApi, hf_hub_download
|
|
|
|
| 9 |
|
| 10 |
from competitions.enums import SubmissionStatus
|
| 11 |
from competitions.errors import AuthenticationError, PastDeadlineError, SubmissionError, SubmissionLimitError
|
| 12 |
-
from competitions.utils import token_information
|
| 13 |
|
| 14 |
|
| 15 |
@dataclass
|
|
@@ -121,8 +122,7 @@ class Submissions:
|
|
| 121 |
if current_datetime > self.end_date:
|
| 122 |
raise PastDeadlineError("Competition has ended.")
|
| 123 |
|
| 124 |
-
|
| 125 |
-
team_id = self._get_team_id(user_info, create_team=False)
|
| 126 |
team_submission_info = self._download_team_submissions(team_id)
|
| 127 |
|
| 128 |
for sub in team_submission_info["submissions"]:
|
|
@@ -134,11 +134,14 @@ class Submissions:
|
|
| 134 |
self._upload_team_submissions(team_id, team_submission_info)
|
| 135 |
|
| 136 |
def _get_team_subs(self, team_id, private=False):
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
| 138 |
submissions_df = pd.DataFrame(team_submissions_info["submissions"])
|
| 139 |
|
| 140 |
if len(submissions_df) == 0:
|
| 141 |
-
return pd.DataFrame()
|
| 142 |
|
| 143 |
if not private:
|
| 144 |
submissions_df = submissions_df.drop(columns=["private_score"])
|
|
@@ -166,46 +169,16 @@ class Submissions:
|
|
| 166 |
return user_info
|
| 167 |
|
| 168 |
def my_submissions(self, user_token):
|
| 169 |
-
user_info = self._get_user_info(user_token)
|
| 170 |
current_date_time = datetime.now()
|
| 171 |
private = False
|
| 172 |
if current_date_time >= self.end_date:
|
| 173 |
private = True
|
| 174 |
-
team_id =
|
| 175 |
if not team_id:
|
| 176 |
return pd.DataFrame()
|
| 177 |
return self._get_team_subs(team_id, private=private)
|
| 178 |
|
| 179 |
-
def
|
| 180 |
-
team_metadata = hf_hub_download(
|
| 181 |
-
repo_id=self.competition_id,
|
| 182 |
-
filename="teams.json",
|
| 183 |
-
token=self.token,
|
| 184 |
-
repo_type="dataset",
|
| 185 |
-
)
|
| 186 |
-
|
| 187 |
-
with open(team_metadata, "r", encoding="utf-8") as f:
|
| 188 |
-
team_metadata = json.load(f)
|
| 189 |
-
|
| 190 |
-
# create a new team, if user is not in any team
|
| 191 |
-
team_id = str(uuid.uuid4())
|
| 192 |
-
user_team[user_id] = team_id
|
| 193 |
-
|
| 194 |
-
team_metadata[team_id] = {
|
| 195 |
-
"id": team_id,
|
| 196 |
-
"name": user_name,
|
| 197 |
-
"members": [user_id],
|
| 198 |
-
"leader": user_id,
|
| 199 |
-
}
|
| 200 |
-
|
| 201 |
-
user_team_json = json.dumps(user_team, indent=4)
|
| 202 |
-
user_team_json_bytes = user_team_json.encode("utf-8")
|
| 203 |
-
user_team_json_buffer = io.BytesIO(user_team_json_bytes)
|
| 204 |
-
|
| 205 |
-
team_metadata_json = json.dumps(team_metadata, indent=4)
|
| 206 |
-
team_metadata_json_bytes = team_metadata_json.encode("utf-8")
|
| 207 |
-
team_metadata_json_buffer = io.BytesIO(team_metadata_json_bytes)
|
| 208 |
-
|
| 209 |
team_submission_info = {}
|
| 210 |
team_submission_info["id"] = team_id
|
| 211 |
team_submission_info["submissions"] = []
|
|
@@ -214,18 +187,6 @@ class Submissions:
|
|
| 214 |
team_submission_info_json_buffer = io.BytesIO(team_submission_info_json_bytes)
|
| 215 |
|
| 216 |
api = HfApi(token=self.token)
|
| 217 |
-
api.upload_file(
|
| 218 |
-
path_or_fileobj=user_team_json_buffer,
|
| 219 |
-
path_in_repo="user_team.json",
|
| 220 |
-
repo_id=self.competition_id,
|
| 221 |
-
repo_type="dataset",
|
| 222 |
-
)
|
| 223 |
-
api.upload_file(
|
| 224 |
-
path_or_fileobj=team_metadata_json_buffer,
|
| 225 |
-
path_in_repo="teams.json",
|
| 226 |
-
repo_id=self.competition_id,
|
| 227 |
-
repo_type="dataset",
|
| 228 |
-
)
|
| 229 |
api.upload_file(
|
| 230 |
path_or_fileobj=team_submission_info_json_buffer,
|
| 231 |
path_in_repo=f"submission_info/{team_id}.json",
|
|
@@ -234,34 +195,13 @@ class Submissions:
|
|
| 234 |
)
|
| 235 |
return team_id
|
| 236 |
|
| 237 |
-
def _get_team_id(self, user_info, create_team):
|
| 238 |
-
user_id = user_info["id"]
|
| 239 |
-
user_name = user_info["name"]
|
| 240 |
-
user_team = hf_hub_download(
|
| 241 |
-
repo_id=self.competition_id,
|
| 242 |
-
filename="user_team.json",
|
| 243 |
-
token=self.token,
|
| 244 |
-
repo_type="dataset",
|
| 245 |
-
)
|
| 246 |
-
with open(user_team, "r", encoding="utf-8") as f:
|
| 247 |
-
user_team = json.load(f)
|
| 248 |
-
|
| 249 |
-
if user_id in user_team:
|
| 250 |
-
return user_team[user_id]
|
| 251 |
-
|
| 252 |
-
if create_team is False:
|
| 253 |
-
return None
|
| 254 |
-
|
| 255 |
-
# if user_id is not there in user_team, create a new team
|
| 256 |
-
team_id = self._create_team(user_team, user_id, user_name)
|
| 257 |
-
return team_id
|
| 258 |
-
|
| 259 |
def new_submission(self, user_token, uploaded_file, submission_comment):
|
| 260 |
# verify token
|
| 261 |
user_info = self._get_user_info(user_token)
|
| 262 |
submission_id = str(uuid.uuid4())
|
| 263 |
user_id = user_info["id"]
|
| 264 |
-
team_id =
|
|
|
|
| 265 |
|
| 266 |
# check if team can submit to the competition
|
| 267 |
if self._is_submission_allowed(team_id) is False:
|
|
@@ -288,49 +228,41 @@ class Submissions:
|
|
| 288 |
submission_id=submission_id,
|
| 289 |
submission_comment=submission_comment,
|
| 290 |
)
|
| 291 |
-
|
| 292 |
-
# Download the submission repo and upload it to the competition repo
|
| 293 |
-
# submission_repo = snapshot_download(
|
| 294 |
-
# repo_id=uploaded_file,
|
| 295 |
-
# local_dir=submission_id,
|
| 296 |
-
# token=user_token,
|
| 297 |
-
# repo_type="model",
|
| 298 |
-
# )
|
| 299 |
-
# api = HfApi(token=self.token)
|
| 300 |
-
# competition_user = self.competition_id.split("/")[0]
|
| 301 |
-
# api.create_repo(
|
| 302 |
-
# repo_id=f"{competition_user}/{submission_id}",
|
| 303 |
-
# repo_type="model",
|
| 304 |
-
# private=True,
|
| 305 |
-
# )
|
| 306 |
-
# api.upload_folder(
|
| 307 |
-
# folder_path=submission_repo,
|
| 308 |
-
# repo_id=f"{competition_user}/{submission_id}",
|
| 309 |
-
# repo_type="model",
|
| 310 |
-
# )
|
| 311 |
-
# create barebones submission runner space
|
| 312 |
-
user_api = HfApi(token=user_token)
|
| 313 |
-
# submission_id is the sha of the submitted model repo + "__" + submission_id
|
| 314 |
-
submission_id = user_api.model_info(repo_id=uploaded_file).sha + "__" + submission_id
|
| 315 |
-
competition_organizer = self.competition_id.split("/")[0]
|
| 316 |
-
space_id = f"{competition_organizer}/comp-{submission_id}"
|
| 317 |
-
api = HfApi(token=self.token)
|
| 318 |
-
api.create_repo(
|
| 319 |
-
repo_id=space_id,
|
| 320 |
-
repo_type="space",
|
| 321 |
-
space_sdk="docker",
|
| 322 |
-
space_hardware=self.hardware,
|
| 323 |
-
private=True,
|
| 324 |
-
)
|
| 325 |
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
remaining_submissions = self.submission_limit - submissions_made
|
| 336 |
return remaining_submissions
|
|
|
|
| 6 |
|
| 7 |
import pandas as pd
|
| 8 |
from huggingface_hub import HfApi, hf_hub_download
|
| 9 |
+
from huggingface_hub.utils._errors import EntryNotFoundError
|
| 10 |
|
| 11 |
from competitions.enums import SubmissionStatus
|
| 12 |
from competitions.errors import AuthenticationError, PastDeadlineError, SubmissionError, SubmissionLimitError
|
| 13 |
+
from competitions.utils import token_information, team_file_api, user_token_api
|
| 14 |
|
| 15 |
|
| 16 |
@dataclass
|
|
|
|
| 122 |
if current_datetime > self.end_date:
|
| 123 |
raise PastDeadlineError("Competition has ended.")
|
| 124 |
|
| 125 |
+
team_id = team_file_api.get_team_info(user_token)["id"]
|
|
|
|
| 126 |
team_submission_info = self._download_team_submissions(team_id)
|
| 127 |
|
| 128 |
for sub in team_submission_info["submissions"]:
|
|
|
|
| 134 |
self._upload_team_submissions(team_id, team_submission_info)
|
| 135 |
|
| 136 |
def _get_team_subs(self, team_id, private=False):
|
| 137 |
+
try:
|
| 138 |
+
team_submissions_info = self._download_team_submissions(team_id)
|
| 139 |
+
except EntryNotFoundError:
|
| 140 |
+
return pd.DataFrame()
|
| 141 |
submissions_df = pd.DataFrame(team_submissions_info["submissions"])
|
| 142 |
|
| 143 |
if len(submissions_df) == 0:
|
| 144 |
+
return pd.DataFrame()
|
| 145 |
|
| 146 |
if not private:
|
| 147 |
submissions_df = submissions_df.drop(columns=["private_score"])
|
|
|
|
| 169 |
return user_info
|
| 170 |
|
| 171 |
def my_submissions(self, user_token):
|
|
|
|
| 172 |
current_date_time = datetime.now()
|
| 173 |
private = False
|
| 174 |
if current_date_time >= self.end_date:
|
| 175 |
private = True
|
| 176 |
+
team_id = team_file_api.get_team_info(user_token)["id"]
|
| 177 |
if not team_id:
|
| 178 |
return pd.DataFrame()
|
| 179 |
return self._get_team_subs(team_id, private=private)
|
| 180 |
|
| 181 |
+
def _create_submission(self, team_id: str):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
team_submission_info = {}
|
| 183 |
team_submission_info["id"] = team_id
|
| 184 |
team_submission_info["submissions"] = []
|
|
|
|
| 187 |
team_submission_info_json_buffer = io.BytesIO(team_submission_info_json_bytes)
|
| 188 |
|
| 189 |
api = HfApi(token=self.token)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
api.upload_file(
|
| 191 |
path_or_fileobj=team_submission_info_json_buffer,
|
| 192 |
path_in_repo=f"submission_info/{team_id}.json",
|
|
|
|
| 195 |
)
|
| 196 |
return team_id
|
| 197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
def new_submission(self, user_token, uploaded_file, submission_comment):
|
| 199 |
# verify token
|
| 200 |
user_info = self._get_user_info(user_token)
|
| 201 |
submission_id = str(uuid.uuid4())
|
| 202 |
user_id = user_info["id"]
|
| 203 |
+
team_id = team_file_api.get_team_info(user_token)["id"]
|
| 204 |
+
self._create_submission(team_id)
|
| 205 |
|
| 206 |
# check if team can submit to the competition
|
| 207 |
if self._is_submission_allowed(team_id) is False:
|
|
|
|
| 228 |
submission_id=submission_id,
|
| 229 |
submission_comment=submission_comment,
|
| 230 |
)
|
| 231 |
+
return self.submission_limit - submissions_made
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
|
| 233 |
+
user_api = HfApi(token=user_token)
|
| 234 |
+
# submission_id is the sha of the submitted model repo + "__" + submission_id
|
| 235 |
+
submission_id = user_api.model_info(repo_id=uploaded_file).sha + "__" + submission_id
|
| 236 |
+
competition_organizer = self.competition_id.split("/")[0]
|
| 237 |
+
space_id = f"{competition_organizer}/comp-{submission_id}"
|
| 238 |
+
server_space_id = space_id + "-server"
|
| 239 |
+
client_space_id = space_id + "-client"
|
| 240 |
+
|
| 241 |
+
api = HfApi(token=self.token)
|
| 242 |
+
api.create_repo(
|
| 243 |
+
repo_id=server_space_id,
|
| 244 |
+
repo_type="space",
|
| 245 |
+
space_sdk="docker",
|
| 246 |
+
space_hardware=self.hardware,
|
| 247 |
+
private=False,
|
| 248 |
+
)
|
| 249 |
+
api.create_repo(
|
| 250 |
+
repo_id=client_space_id,
|
| 251 |
+
repo_type="space",
|
| 252 |
+
space_sdk="docker",
|
| 253 |
+
space_hardware=self.hardware,
|
| 254 |
+
private=True,
|
| 255 |
+
)
|
| 256 |
+
user_token_api.put(team_id, user_token)
|
| 257 |
+
|
| 258 |
+
# api.add_space_secret(repo_id=space_id, key="USER_TOKEN", value=user_token)
|
| 259 |
+
submissions_made = self._increment_submissions(
|
| 260 |
+
team_id=team_id,
|
| 261 |
+
user_id=user_id,
|
| 262 |
+
submission_id=submission_id,
|
| 263 |
+
submission_comment=submission_comment,
|
| 264 |
+
submission_repo=uploaded_file,
|
| 265 |
+
space_id=space_id,
|
| 266 |
+
)
|
| 267 |
remaining_submissions = self.submission_limit - submissions_made
|
| 268 |
return remaining_submissions
|
competitions/templates/index.html
CHANGED
|
@@ -140,15 +140,16 @@
|
|
| 140 |
})
|
| 141 |
.then(data => {
|
| 142 |
const contentDiv = document.getElementById('content');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
// console.log(data.response.submissions);
|
| 144 |
// contentDiv.innerHTML = marked.parse(data.response.submission_text) + data.response.submissions;
|
| 145 |
if (data.response.submissions && data.response.submissions.length > 0 && data.response.error.length == 0) {
|
| 146 |
// Start building the table HTML
|
| 147 |
-
let tableHTML =
|
| 148 |
-
<div class="flex items-center">
|
| 149 |
-
<input type="text" name="team_name" id="team_name" class="mt-1 mb-1 block me-2" value="${data.response.team_name}">
|
| 150 |
-
<button id="updateTeamNameButton" type="button" class="confirm text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2">Update Team Name</button>
|
| 151 |
-
</div>`;
|
| 152 |
tableHTML += '<table border="1"><tr><th>Datetime</th><th>Submission ID</th><th>Public Score</th><th>Submission Comment</th><th>Selected</th><th>Status</th></tr>';
|
| 153 |
|
| 154 |
// Iterate over each submission and add it to the table
|
|
@@ -171,13 +172,13 @@
|
|
| 171 |
document.getElementById('updateSelectedSubmissionsButton').addEventListener('click', function () {
|
| 172 |
updateSelectedSubmissions();
|
| 173 |
});
|
| 174 |
-
document.getElementById('updateTeamNameButton').addEventListener('click', function () {
|
| 175 |
-
updateTeamName();
|
| 176 |
-
});
|
| 177 |
} else {
|
| 178 |
// Display message if there are no submissions
|
| 179 |
-
contentDiv.innerHTML = marked.parse(data.response.submission_text) + marked.parse(data.response.error);
|
| 180 |
}
|
|
|
|
|
|
|
|
|
|
| 181 |
articleLoadingSpinner.classList.add('hidden');
|
| 182 |
})
|
| 183 |
.catch(error => {
|
|
@@ -342,12 +343,25 @@
|
|
| 342 |
|
| 343 |
function checkOAuth() {
|
| 344 |
var url = "/login_status";
|
| 345 |
-
makeApiRequest(url, function (
|
| 346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
document.getElementById("loginButton").style.display = "block";
|
| 348 |
document.getElementById("logoutButton").style.display = "none";
|
| 349 |
-
}
|
| 350 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
document.getElementById("logoutButton").style.display = "block";
|
| 352 |
}
|
| 353 |
});
|
|
@@ -459,19 +473,19 @@
|
|
| 459 |
</li>
|
| 460 |
<li>
|
| 461 |
<a href="#" id="my_submissions"
|
| 462 |
-
class="flex items-center w-full p-2
|
| 463 |
submissions</a>
|
| 464 |
</li>
|
| 465 |
<li>
|
| 466 |
<a href="#" id="new_submission"
|
| 467 |
-
class="flex items-center w-full p-2
|
| 468 |
submission</a>
|
| 469 |
</li>
|
| 470 |
</ul>
|
| 471 |
</li>
|
| 472 |
<li>
|
| 473 |
<a href="#" id="admin"
|
| 474 |
-
class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 group">
|
| 475 |
<svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 group-hover:text-gray-900"
|
| 476 |
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor"
|
| 477 |
viewBox="0 0 24 24">
|
|
@@ -481,6 +495,12 @@
|
|
| 481 |
<span class="flex-1 ms-3 whitespace-nowrap">Admin</span>
|
| 482 |
</a>
|
| 483 |
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
<li id="loginButton" style="display: none;">
|
| 485 |
<a href="/login/huggingface"
|
| 486 |
class="flex justify-center items-center bg-blue-400 hover:bg-blue-600 text-white text-center font-bold py-2 px-4 rounded transition duration-200 ease-in-out">
|
|
@@ -650,6 +670,53 @@
|
|
| 650 |
</div>
|
| 651 |
</div>
|
| 652 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.js"></script>
|
| 654 |
<script>
|
| 655 |
document.addEventListener("DOMContentLoaded", function () {
|
|
@@ -948,4 +1015,60 @@
|
|
| 948 |
});
|
| 949 |
</script>
|
| 950 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 951 |
</html>
|
|
|
|
| 140 |
})
|
| 141 |
.then(data => {
|
| 142 |
const contentDiv = document.getElementById('content');
|
| 143 |
+
const teamNameDiv = `
|
| 144 |
+
<div class="flex items-center">
|
| 145 |
+
<input type="text" name="team_name" id="team_name" class="mt-1 mb-1 block me-2" value="${data.response.team_name}">
|
| 146 |
+
<button id="updateTeamNameButton" type="button" class="confirm text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2">Update Team Name</button>
|
| 147 |
+
</div>`;
|
| 148 |
// console.log(data.response.submissions);
|
| 149 |
// contentDiv.innerHTML = marked.parse(data.response.submission_text) + data.response.submissions;
|
| 150 |
if (data.response.submissions && data.response.submissions.length > 0 && data.response.error.length == 0) {
|
| 151 |
// Start building the table HTML
|
| 152 |
+
let tableHTML = teamNameDiv;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
tableHTML += '<table border="1"><tr><th>Datetime</th><th>Submission ID</th><th>Public Score</th><th>Submission Comment</th><th>Selected</th><th>Status</th></tr>';
|
| 154 |
|
| 155 |
// Iterate over each submission and add it to the table
|
|
|
|
| 172 |
document.getElementById('updateSelectedSubmissionsButton').addEventListener('click', function () {
|
| 173 |
updateSelectedSubmissions();
|
| 174 |
});
|
|
|
|
|
|
|
|
|
|
| 175 |
} else {
|
| 176 |
// Display message if there are no submissions
|
| 177 |
+
contentDiv.innerHTML = teamNameDiv + marked.parse(data.response.submission_text) + marked.parse(data.response.error);
|
| 178 |
}
|
| 179 |
+
document.getElementById('updateTeamNameButton').addEventListener('click', function () {
|
| 180 |
+
updateTeamName();
|
| 181 |
+
});
|
| 182 |
articleLoadingSpinner.classList.add('hidden');
|
| 183 |
})
|
| 184 |
.catch(error => {
|
|
|
|
| 343 |
|
| 344 |
function checkOAuth() {
|
| 345 |
var url = "/login_status";
|
| 346 |
+
makeApiRequest(url, function ({is_login, is_admin, is_registered}) {
|
| 347 |
+
const registerRemoveStyle = ["pointer-events-none", "text-gray-400", "cursor-not-allowed"];
|
| 348 |
+
const registerNewStyle = ["ext-gray-900", "hover:bg-gray-100"]
|
| 349 |
+
if (is_login) {
|
| 350 |
+
document.getElementById("loginButton").style.display = "none";
|
| 351 |
+
document.getElementById("logoutButton").style.display = "block";
|
| 352 |
+
} else {
|
| 353 |
document.getElementById("loginButton").style.display = "block";
|
| 354 |
document.getElementById("logoutButton").style.display = "none";
|
| 355 |
+
}
|
| 356 |
+
if (is_admin) {
|
| 357 |
+
document.getElementById("admin").classList.remove("hidden");
|
| 358 |
+
}
|
| 359 |
+
if (is_registered) {
|
| 360 |
+
document.getElementById("my_submissions").classList.remove(...registerRemoveStyle);
|
| 361 |
+
document.getElementById("my_submissions").classList.add(...registerNewStyle);
|
| 362 |
+
document.getElementById("new_submission").classList.remove(...registerRemoveStyle);
|
| 363 |
+
document.getElementById("new_submission").classList.add(...registerNewStyle);
|
| 364 |
+
} else {
|
| 365 |
document.getElementById("logoutButton").style.display = "block";
|
| 366 |
}
|
| 367 |
});
|
|
|
|
| 473 |
</li>
|
| 474 |
<li>
|
| 475 |
<a href="#" id="my_submissions"
|
| 476 |
+
class="flex items-center w-full p-2 transition duration-75 rounded-lg pl-11 group pointer-events-none text-gray-400 cursor-not-allowed">My
|
| 477 |
submissions</a>
|
| 478 |
</li>
|
| 479 |
<li>
|
| 480 |
<a href="#" id="new_submission"
|
| 481 |
+
class="flex items-center w-full p-2 transition duration-75 rounded-lg pl-11 group pointer-events-none text-gray-400 cursor-not-allowed">New
|
| 482 |
submission</a>
|
| 483 |
</li>
|
| 484 |
</ul>
|
| 485 |
</li>
|
| 486 |
<li>
|
| 487 |
<a href="#" id="admin"
|
| 488 |
+
class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 group hidden">
|
| 489 |
<svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 group-hover:text-gray-900"
|
| 490 |
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor"
|
| 491 |
viewBox="0 0 24 24">
|
|
|
|
| 495 |
<span class="flex-1 ms-3 whitespace-nowrap">Admin</span>
|
| 496 |
</a>
|
| 497 |
</li>
|
| 498 |
+
<li id="registerButton" style="display: none;">
|
| 499 |
+
<a href="#"
|
| 500 |
+
class="flex justify-center items-center bg-blue-400 hover:bg-blue-600 text-white text-center font-bold py-2 px-4 rounded transition duration-200 ease-in-out">
|
| 501 |
+
Register
|
| 502 |
+
</a>
|
| 503 |
+
</li>
|
| 504 |
<li id="loginButton" style="display: none;">
|
| 505 |
<a href="/login/huggingface"
|
| 506 |
class="flex justify-center items-center bg-blue-400 hover:bg-blue-600 text-white text-center font-bold py-2 px-4 rounded transition duration-200 ease-in-out">
|
|
|
|
| 670 |
</div>
|
| 671 |
</div>
|
| 672 |
</div>
|
| 673 |
+
<div id="register-modal" tabindex="-1"
|
| 674 |
+
class="hidden fixed inset-0 z-40 flex items-center justify-center w-full h-full bg-black bg-opacity-50">
|
| 675 |
+
<div class="relative w-full max-w-xl p-4">
|
| 676 |
+
<div class="relative bg-white rounded-lg shadow-2xl">
|
| 677 |
+
<button type="button"
|
| 678 |
+
class="absolute top-3 right-3 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 inline-flex justify-center items-center"
|
| 679 |
+
data-modal-hide="register-modal">
|
| 680 |
+
<svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
| 681 |
+
viewBox="0 0 14 14">
|
| 682 |
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
| 683 |
+
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
|
| 684 |
+
</svg>
|
| 685 |
+
<span class="sr-only">Close</span>
|
| 686 |
+
</button>
|
| 687 |
+
<div class="p-6 md:p-8 text-center">
|
| 688 |
+
<form id="registerForm" class="max-w-md mx-auto p-6 bg-white rounded-lg shadow space-y-4">
|
| 689 |
+
<div class="grid grid-cols-3 items-center gap-4">
|
| 690 |
+
<label for="teamname" class="text-sm font-medium text-gray-700 text-right">Team Name</label>
|
| 691 |
+
<input type="text" id="teamname" name="teamname"
|
| 692 |
+
class="col-span-2 px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
| 693 |
+
</div>
|
| 694 |
+
|
| 695 |
+
<div class="grid grid-cols-3 items-center gap-4">
|
| 696 |
+
<label for="reigsterFile" class="text-sm font-medium text-gray-700 text-right">Team Datas</label>
|
| 697 |
+
<input type="file" id="reigsterFile" name="reigsterFile"
|
| 698 |
+
class="col-span-2 text-sm text-gray-500 file:mr-4 file:py-2 file:px-4
|
| 699 |
+
file:rounded-md file:border-0
|
| 700 |
+
file:text-sm file:font-semibold
|
| 701 |
+
file:bg-blue-50 file:text-blue-700
|
| 702 |
+
hover:file:bg-blue-100">
|
| 703 |
+
</div>
|
| 704 |
+
<p class="mt-1 text-xs text-gray-500">
|
| 705 |
+
Please upload a .xlsx file with the following format: <br>
|
| 706 |
+
<a href="/register_template_file" download class="text-blue-600 hover:underline">Download here</a>.
|
| 707 |
+
</p>
|
| 708 |
+
<p id="errorMessage" class="text-red-600 text-sm hidden"></p>
|
| 709 |
+
<button type="submit"
|
| 710 |
+
id="submitRegisterButton"
|
| 711 |
+
class="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition disabled:bg-blue-800 disabled:opacity-80 disabled:cursor-not-allowed">
|
| 712 |
+
Submit
|
| 713 |
+
</button>
|
| 714 |
+
</form>
|
| 715 |
+
|
| 716 |
+
</div>
|
| 717 |
+
</div>
|
| 718 |
+
</div>
|
| 719 |
+
</div>
|
| 720 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.js"></script>
|
| 721 |
<script>
|
| 722 |
document.addEventListener("DOMContentLoaded", function () {
|
|
|
|
| 1015 |
});
|
| 1016 |
</script>
|
| 1017 |
|
| 1018 |
+
|
| 1019 |
+
<script>
|
| 1020 |
+
document.getElementById("registerButton").addEventListener("click", function () {
|
| 1021 |
+
document.getElementById("register-modal").classList.remove("hidden");
|
| 1022 |
+
});
|
| 1023 |
+
|
| 1024 |
+
document.querySelector("[data-modal-hide='register-modal']").addEventListener("click", function () {
|
| 1025 |
+
document.getElementById("register-modal").classList.add("hidden");
|
| 1026 |
+
});
|
| 1027 |
+
|
| 1028 |
+
document.getElementById('registerForm').addEventListener('submit', function (e) {
|
| 1029 |
+
e.preventDefault();
|
| 1030 |
+
|
| 1031 |
+
const teamname = document.getElementById('teamname').value.trim();
|
| 1032 |
+
const fileInput = document.getElementById('reigsterFile');
|
| 1033 |
+
const file = fileInput.files[0];
|
| 1034 |
+
const errorMessage = document.getElementById('errorMessage');
|
| 1035 |
+
const submitBtn = document.getElementById('submitRegisterButton');
|
| 1036 |
+
|
| 1037 |
+
submitBtn.disabled = true;
|
| 1038 |
+
submitBtn.textContent = 'Submiting...';
|
| 1039 |
+
|
| 1040 |
+
errorMessage.textContent = '';
|
| 1041 |
+
errorMessage.classList.add('hidden');
|
| 1042 |
+
|
| 1043 |
+
if (!teamname) {
|
| 1044 |
+
errorMessage.textContent = 'Team name is required.';
|
| 1045 |
+
errorMessage.classList.remove('hidden');
|
| 1046 |
+
return;
|
| 1047 |
+
}
|
| 1048 |
+
|
| 1049 |
+
if (!file) {
|
| 1050 |
+
errorMessage.textContent = 'Please upload a file.';
|
| 1051 |
+
errorMessage.classList.remove('hidden');
|
| 1052 |
+
return;
|
| 1053 |
+
}
|
| 1054 |
+
|
| 1055 |
+
const formData = new FormData();
|
| 1056 |
+
formData.append('teamname', teamname);
|
| 1057 |
+
formData.append('file', file);
|
| 1058 |
+
fetch('/register', { method: 'POST', body: formData })
|
| 1059 |
+
.then(response => response.json())
|
| 1060 |
+
.then(data => {
|
| 1061 |
+
if (data.success) {
|
| 1062 |
+
document.getElementById('register-modal').classList.add("hidden");
|
| 1063 |
+
window.location.reload();
|
| 1064 |
+
} else {
|
| 1065 |
+
submitBtn.disabled = false;
|
| 1066 |
+
submitBtn.textContent = 'Submit';
|
| 1067 |
+
errorMessage.textContent = data.response || 'An error occurred.';
|
| 1068 |
+
errorMessage.classList.remove('hidden');
|
| 1069 |
+
}
|
| 1070 |
+
})
|
| 1071 |
+
});
|
| 1072 |
+
</script>
|
| 1073 |
+
|
| 1074 |
</html>
|
competitions/utils.py
CHANGED
|
@@ -4,11 +4,16 @@ import os
|
|
| 4 |
import shlex
|
| 5 |
import subprocess
|
| 6 |
import traceback
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
import requests
|
| 9 |
from fastapi import Request
|
| 10 |
from huggingface_hub import HfApi, hf_hub_download
|
| 11 |
from loguru import logger
|
|
|
|
| 12 |
|
| 13 |
from competitions.enums import SubmissionStatus
|
| 14 |
from competitions.params import EvalParams
|
|
@@ -316,71 +321,203 @@ def is_user_admin(user_token, competition_organization):
|
|
| 316 |
return False
|
| 317 |
|
| 318 |
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
user_team = hf_hub_download(
|
| 323 |
-
repo_id=competition_id,
|
| 324 |
-
filename="user_team.json",
|
| 325 |
-
token=hf_token,
|
| 326 |
-
repo_type="dataset",
|
| 327 |
-
)
|
| 328 |
-
with open(user_team, "r", encoding="utf-8") as f:
|
| 329 |
-
user_team = json.load(f)
|
| 330 |
|
| 331 |
-
|
| 332 |
-
|
|
|
|
|
|
|
|
|
|
| 333 |
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
filename="teams.json",
|
| 339 |
-
token=hf_token,
|
| 340 |
-
repo_type="dataset",
|
| 341 |
-
)
|
| 342 |
-
with open(team_metadata, "r", encoding="utf-8") as f:
|
| 343 |
-
team_metadata = json.load(f)
|
| 344 |
|
| 345 |
-
|
| 346 |
-
|
| 347 |
|
|
|
|
| 348 |
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
token=hf_token,
|
| 356 |
-
repo_type="dataset",
|
| 357 |
-
)
|
| 358 |
-
with open(user_team, "r", encoding="utf-8") as f:
|
| 359 |
-
user_team = json.load(f)
|
| 360 |
|
| 361 |
-
|
| 362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
|
| 364 |
-
team_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
team_metadata_json_bytes = team_metadata_json.encode("utf-8")
|
| 378 |
-
team_metadata_json_buffer = io.BytesIO(team_metadata_json_bytes)
|
| 379 |
-
api = HfApi(token=hf_token)
|
| 380 |
-
api.upload_file(
|
| 381 |
-
path_or_fileobj=team_metadata_json_buffer,
|
| 382 |
-
path_in_repo="teams.json",
|
| 383 |
-
repo_id=competition_id,
|
| 384 |
-
repo_type="dataset",
|
| 385 |
-
)
|
| 386 |
-
return new_team_name
|
|
|
|
| 4 |
import shlex
|
| 5 |
import subprocess
|
| 6 |
import traceback
|
| 7 |
+
import threading
|
| 8 |
+
import uuid
|
| 9 |
+
import base64
|
| 10 |
+
from typing import Optional, Dict, Any
|
| 11 |
|
| 12 |
import requests
|
| 13 |
from fastapi import Request
|
| 14 |
from huggingface_hub import HfApi, hf_hub_download
|
| 15 |
from loguru import logger
|
| 16 |
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
| 17 |
|
| 18 |
from competitions.enums import SubmissionStatus
|
| 19 |
from competitions.params import EvalParams
|
|
|
|
| 321 |
return False
|
| 322 |
|
| 323 |
|
| 324 |
+
class TeamAlreadyExistsError(Exception):
|
| 325 |
+
"""Custom exception for when a team already exists."""
|
| 326 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 327 |
|
| 328 |
+
class TeamFileApi:
|
| 329 |
+
def __init__(self, hf_token: str, competition_id: str):
|
| 330 |
+
self.hf_token = hf_token
|
| 331 |
+
self.competition_id = competition_id
|
| 332 |
+
self._lock = threading.Lock()
|
| 333 |
|
| 334 |
+
def _get_team_info(self, user_id: str) -> Optional[Dict[str, Any]]:
|
| 335 |
+
user_team = hf_hub_download(
|
| 336 |
+
repo_id=self.competition_id,
|
| 337 |
+
filename="user_team.json",
|
| 338 |
+
token=self.hf_token,
|
| 339 |
+
repo_type="dataset",
|
| 340 |
+
)
|
| 341 |
|
| 342 |
+
with open(user_team, "r", encoding="utf-8") as f:
|
| 343 |
+
user_team = json.load(f)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 344 |
|
| 345 |
+
if user_id not in user_team:
|
| 346 |
+
return None
|
| 347 |
|
| 348 |
+
team_id = user_team[user_id]
|
| 349 |
|
| 350 |
+
team_metadata = hf_hub_download(
|
| 351 |
+
repo_id=self.competition_id,
|
| 352 |
+
filename="teams.json",
|
| 353 |
+
token=self.hf_token,
|
| 354 |
+
repo_type="dataset",
|
| 355 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
|
| 357 |
+
with open(team_metadata, "r", encoding="utf-8") as f:
|
| 358 |
+
team_metadata = json.load(f)
|
| 359 |
+
|
| 360 |
+
return team_metadata[team_id]
|
| 361 |
+
|
| 362 |
+
def _create_team(self, user_id: str, team_name: str, team_file: Optional[io.BytesIO]) -> str:
|
| 363 |
+
with self._lock:
|
| 364 |
+
user_team = hf_hub_download(
|
| 365 |
+
repo_id=self.competition_id,
|
| 366 |
+
filename="user_team.json",
|
| 367 |
+
token=self.hf_token,
|
| 368 |
+
repo_type="dataset",
|
| 369 |
+
)
|
| 370 |
+
with open(user_team, "r", encoding="utf-8") as f:
|
| 371 |
+
user_team = json.load(f)
|
| 372 |
+
|
| 373 |
+
team_metadata = hf_hub_download(
|
| 374 |
+
repo_id=self.competition_id,
|
| 375 |
+
filename="teams.json",
|
| 376 |
+
token=self.hf_token,
|
| 377 |
+
repo_type="dataset",
|
| 378 |
+
)
|
| 379 |
+
|
| 380 |
+
with open(team_metadata, "r", encoding="utf-8") as f:
|
| 381 |
+
team_metadata = json.load(f)
|
| 382 |
+
|
| 383 |
+
# create a new team, if user is not in any team
|
| 384 |
+
team_id = str(uuid.uuid4())
|
| 385 |
+
user_team[user_id] = team_id
|
| 386 |
+
|
| 387 |
+
team_metadata[team_id] = {
|
| 388 |
+
"id": team_id,
|
| 389 |
+
"name": team_name,
|
| 390 |
+
"members": [user_id],
|
| 391 |
+
"leader": user_id,
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
user_team_json = json.dumps(user_team, indent=4)
|
| 395 |
+
user_team_json_bytes = user_team_json.encode("utf-8")
|
| 396 |
+
user_team_json_buffer = io.BytesIO(user_team_json_bytes)
|
| 397 |
+
|
| 398 |
+
team_metadata_json = json.dumps(team_metadata, indent=4)
|
| 399 |
+
team_metadata_json_bytes = team_metadata_json.encode("utf-8")
|
| 400 |
+
team_metadata_json_buffer = io.BytesIO(team_metadata_json_bytes)
|
| 401 |
+
|
| 402 |
+
api = HfApi(token=self.hf_token)
|
| 403 |
+
api.upload_file(
|
| 404 |
+
path_or_fileobj=user_team_json_buffer,
|
| 405 |
+
path_in_repo="user_team.json",
|
| 406 |
+
repo_id=self.competition_id,
|
| 407 |
+
repo_type="dataset",
|
| 408 |
+
)
|
| 409 |
+
api.upload_file(
|
| 410 |
+
path_or_fileobj=team_metadata_json_buffer,
|
| 411 |
+
path_in_repo="teams.json",
|
| 412 |
+
repo_id=self.competition_id,
|
| 413 |
+
repo_type="dataset",
|
| 414 |
+
)
|
| 415 |
+
|
| 416 |
+
if team_file is not None:
|
| 417 |
+
resp = api.upload_file(
|
| 418 |
+
path_or_fileobj=team_file,
|
| 419 |
+
path_in_repo=f"team_datas/{team_id}.xlsx",
|
| 420 |
+
repo_id=self.competition_id,
|
| 421 |
+
repo_type="dataset",
|
| 422 |
+
)
|
| 423 |
+
return team_id
|
| 424 |
+
|
| 425 |
+
def create_team(self, user_token: str, team_name: str, team_file: io.BytesIO) -> str:
|
| 426 |
+
user_info = token_information(token=user_token)
|
| 427 |
+
return self._create_team(user_info["id"], team_name, team_file)
|
| 428 |
+
|
| 429 |
+
def get_team_info(self, user_token: str) -> Optional[Dict[str, Any]]:
|
| 430 |
+
user_info = token_information(token=user_token)
|
| 431 |
+
return self._get_team_info(user_info["id"])
|
| 432 |
+
|
| 433 |
+
def update_and_create_team_name(self, user_token, new_team_name):
|
| 434 |
+
user_info = token_information(token=user_token)
|
| 435 |
+
user_id = user_info["id"]
|
| 436 |
+
team_info = self._get_team_info(user_id)
|
| 437 |
+
if team_info is None:
|
| 438 |
+
self._create_team(user_id, new_team_name)
|
| 439 |
+
return new_team_name
|
| 440 |
+
|
| 441 |
+
with self._lock:
|
| 442 |
+
team_metadata = hf_hub_download(
|
| 443 |
+
repo_id=self.competition_id,
|
| 444 |
+
filename="teams.json",
|
| 445 |
+
token=self.hf_token,
|
| 446 |
+
repo_type="dataset",
|
| 447 |
+
)
|
| 448 |
+
with open(team_metadata, "r", encoding="utf-8") as f:
|
| 449 |
+
team_metadata = json.load(f)
|
| 450 |
+
|
| 451 |
+
team_metadata[team_info["id"]]["name"] = new_team_name
|
| 452 |
+
team_metadata_json = json.dumps(team_metadata, indent=4)
|
| 453 |
+
team_metadata_json_bytes = team_metadata_json.encode("utf-8")
|
| 454 |
+
team_metadata_json_buffer = io.BytesIO(team_metadata_json_bytes)
|
| 455 |
+
api = HfApi(token=self.hf_token)
|
| 456 |
+
api.upload_file(
|
| 457 |
+
path_or_fileobj=team_metadata_json_buffer,
|
| 458 |
+
path_in_repo="teams.json",
|
| 459 |
+
repo_id=self.competition_id,
|
| 460 |
+
repo_type="dataset",
|
| 461 |
+
)
|
| 462 |
+
return new_team_name
|
| 463 |
+
|
| 464 |
+
|
| 465 |
+
team_file_api = TeamFileApi(
|
| 466 |
+
os.environ.get("HF_TOKEN", None),
|
| 467 |
+
os.environ.get("COMPETITION_ID"),
|
| 468 |
+
)
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
class UserTokenApi:
|
| 472 |
+
def __init__(self, hf_token: str, key_base64: str, competition_id: str):
|
| 473 |
+
self.hf_token = hf_token
|
| 474 |
+
self.key = base64.b64decode(key_base64)
|
| 475 |
+
self.competition_id = competition_id
|
| 476 |
+
|
| 477 |
+
def _encrypt(self, text: str) -> str:
|
| 478 |
+
aesgcm = AESGCM(self.key)
|
| 479 |
+
nonce = os.urandom(12)
|
| 480 |
+
encrypted_data = aesgcm.encrypt(nonce, text.encode(), None)
|
| 481 |
+
return base64.b64encode(nonce + encrypted_data).decode()
|
| 482 |
+
|
| 483 |
+
def _decrypt(self, encrypted_text: str) -> str:
|
| 484 |
+
aesgcm = AESGCM(self.key)
|
| 485 |
+
data = base64.b64decode(encrypted_text)
|
| 486 |
+
nonce = data[:12]
|
| 487 |
+
ciphertext = data[12:]
|
| 488 |
+
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
|
| 489 |
+
return plaintext.decode()
|
| 490 |
+
|
| 491 |
+
def put(self, team_id: str, user_token: str):
|
| 492 |
+
encrypted_token = self._encrypt(user_token)
|
| 493 |
+
api = HfApi(token=self.hf_token)
|
| 494 |
+
api.upload_file(
|
| 495 |
+
path_or_fileobj=io.BytesIO(encrypted_token.encode()),
|
| 496 |
+
path_in_repo=f"team_user_tokens/{team_id}",
|
| 497 |
+
repo_id=self.competition_id,
|
| 498 |
+
repo_type="dataset",
|
| 499 |
+
)
|
| 500 |
|
| 501 |
+
def get(self, team_id: str) -> Optional[str]:
|
| 502 |
+
try:
|
| 503 |
+
user_token = hf_hub_download(
|
| 504 |
+
repo_id=self.competition_id,
|
| 505 |
+
filename=f"team_user_tokens/{team_id}",
|
| 506 |
+
token=self.hf_token,
|
| 507 |
+
repo_type="dataset",
|
| 508 |
+
)
|
| 509 |
+
except Exception as e:
|
| 510 |
+
logger.error(f"Failed to download user token - {e}")
|
| 511 |
+
return None
|
| 512 |
|
| 513 |
+
with open(user_token, "r", encoding="utf-8") as f:
|
| 514 |
+
encrypted_token = f.read()
|
| 515 |
+
|
| 516 |
+
return self._decrypt(encrypted_token)
|
| 517 |
+
|
| 518 |
+
|
| 519 |
+
user_token_api = UserTokenApi(
|
| 520 |
+
os.environ.get("HF_TOKEN", None),
|
| 521 |
+
os.environ.get("USER_TOKEN_KEY_BASE64"),
|
| 522 |
+
os.environ.get("COMPETITION_ID")
|
| 523 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|