Libra-1995 commited on
Commit
1b19224
·
1 Parent(s): d61c80e

feat: update resgiter

Browse files
competitions/app.py CHANGED
@@ -3,7 +3,7 @@ import os
3
  import threading
4
  import time
5
  import io
6
- from typing import Dict, Any
7
  from urllib.parse import urlencode
8
 
9
  from fastapi import Depends, FastAPI, Form, HTTPException, Request, UploadFile, Body, Query
@@ -14,7 +14,7 @@ from huggingface_hub import hf_hub_download
14
  from huggingface_hub.utils import disable_progress_bars
15
  from huggingface_hub.utils._errors import EntryNotFoundError
16
  from loguru import logger
17
- from pydantic import BaseModel
18
  from requests.exceptions import RequestException
19
  from filelock import FileLock, Timeout as FileLockTimeout
20
 
@@ -66,6 +66,47 @@ class UpdateSelectedSubmissionsRequest(BaseModel):
66
  class UpdateTeamNameRequest(BaseModel):
67
  new_team_name: str
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  def run_job_runner():
71
  job_runner = JobRunner(
@@ -461,23 +502,20 @@ def register_example_file():
461
 
462
  @app.post("/register")
463
  def register(
464
- teamname: str = Form(..., max_length=20),
465
- file: UploadFile = Form(...),
466
- email: str = Form(..., max_length=100),
467
  user_token: str = Depends(utils.user_authentication)
468
  ):
469
  if user_token is None:
470
  return {"success": False, "response": "Please login."}
471
 
472
- file_bytes = file.file.read()
473
- file_stream = io.BytesIO(file_bytes)
474
- file_stream.seek(0)
475
 
476
  utils.team_file_api.create_team(
477
  user_token,
478
- teamname,
479
- file_stream,
480
- email
481
  )
482
 
483
  return {"success": True, "response": "Team created successfully."}
@@ -497,3 +535,17 @@ def get_error_log(token: str = Query(..., description="Token to access the error
497
  except Exception as e:
498
  logger.error(f"Error while fetching log file: {e}")
499
  raise HTTPException(status_code=500, detail="Internal server error.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import threading
4
  import time
5
  import io
6
+ from typing import Dict, Any, List, Optional
7
  from urllib.parse import urlencode
8
 
9
  from fastapi import Depends, FastAPI, Form, HTTPException, Request, UploadFile, Body, Query
 
14
  from huggingface_hub.utils import disable_progress_bars
15
  from huggingface_hub.utils._errors import EntryNotFoundError
16
  from loguru import logger
17
+ from pydantic import BaseModel, Field
18
  from requests.exceptions import RequestException
19
  from filelock import FileLock, Timeout as FileLockTimeout
20
 
 
66
  class UpdateTeamNameRequest(BaseModel):
67
  new_team_name: str
68
 
69
+ # "majar": {
70
+ # "type": "string",
71
+ # "title": "Majar",
72
+ # "minLength": 1,
73
+ # },
74
+ # "degree": {
75
+ # "type": "string",
76
+ # "title": "Degree",
77
+ # "enum": ["Bachelor", "Master", "PhD"]
78
+ # },
79
+ # "advising_professor": {
80
+ # "type": "string",
81
+ # "title": "Advising Professo",
82
+ # "minLength": 1,
83
+ # },
84
+ # "grade": {
85
+ # "type": "string",
86
+ # "title": "Grade",
87
+ # "minLength": 1,
88
+ # },
89
+ # "expected_graduation_year": {
90
+ # "type": "string",
91
+ # "title": "Expected Graduation Year",
92
+ # "minLength": 1,
93
+ # }
94
+
95
+ class TeamMember(BaseModel):
96
+ name: str = Field(..., max_length=50, description="Name of the team member")
97
+ email: str = Field(..., max_length=100, description="Email of the team member")
98
+ institution: str = Field(..., max_length=100, description="Institution of the team member")
99
+ is_student: bool = Field(..., description="Is the team member a student?")
100
+ majar: Optional[str] = Field(None, max_length=50, description="Major of the team member")
101
+ degree: Optional[str] = Field(None, description="Degree of the team member")
102
+ advising_professor: Optional[str] = Field(None, max_length=50, description="Advising professor of the team member")
103
+ grade: Optional[str] = Field(None, max_length=50, description="Grade of the team member")
104
+ expected_graduation_year: Optional[str] = Field(None, max_length=4, description="Expected graduation year of the team member")
105
+
106
+ class RegisterRequest(BaseModel):
107
+ team_name: str = Field(..., max_length=20, description="Name of the team")
108
+ team_members: List[TeamMember] = Field(min_length=1, max_length=4, description="List of team members")
109
+
110
 
111
  def run_job_runner():
112
  job_runner = JobRunner(
 
502
 
503
  @app.post("/register")
504
  def register(
505
+ request: RegisterRequest,
 
 
506
  user_token: str = Depends(utils.user_authentication)
507
  ):
508
  if user_token is None:
509
  return {"success": False, "response": "Please login."}
510
 
511
+ team_info = utils.team_file_api.get_team_info(user_token)
512
+ if team_info is not None:
513
+ return {"success": False, "response": "You have already registered your team."}
514
 
515
  utils.team_file_api.create_team(
516
  user_token,
517
+ request.team_name,
518
+ request.model_dump()
 
519
  )
520
 
521
  return {"success": True, "response": "Team created successfully."}
 
535
  except Exception as e:
536
  logger.error(f"Error while fetching log file: {e}")
537
  raise HTTPException(status_code=500, detail="Internal server error.")
538
+
539
+
540
+ @app.get("/register_page", response_class=HTMLResponse)
541
+ def register_page(request: Request):
542
+ """
543
+ This function is used to render the registration HTML page.
544
+ """
545
+ if HF_TOKEN is None:
546
+ return HTTPException(status_code=500, detail="HF_TOKEN is not set.")
547
+ context = {
548
+ "request": request,
549
+ "version": __version__,
550
+ }
551
+ return templates.TemplateResponse("register_page.html", context)
competitions/templates/index.html CHANGED
@@ -687,58 +687,6 @@
687
  </div>
688
  </div>
689
  </div>
690
- <div id="register-modal" tabindex="-1"
691
- class="hidden fixed inset-0 z-40 flex items-center justify-center w-full h-full bg-black bg-opacity-50">
692
- <div class="relative w-full max-w-xl p-4">
693
- <div class="relative bg-white rounded-lg shadow-2xl">
694
- <button type="button"
695
- 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"
696
- data-modal-hide="register-modal">
697
- <svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
698
- viewBox="0 0 14 14">
699
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
700
- d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" />
701
- </svg>
702
- <span class="sr-only">Close</span>
703
- </button>
704
- <div class="p-6 md:p-8 text-center">
705
- <form id="registerForm" class="max-w-md mx-auto p-6 bg-white rounded-lg shadow space-y-4">
706
- <div class="grid grid-cols-3 items-center gap-4">
707
- <label for="teamname" class="text-sm font-medium text-gray-700 text-right">Team Name</label>
708
- <input type="text" id="teamname" name="teamname"
709
- 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">
710
- </div>
711
- <div class="grid grid-cols-3 items-center gap-4">
712
- <label for="email" class="text-sm font-medium text-gray-700 text-right">Email</label>
713
- <input type="text" id="email" name="email"
714
- 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">
715
- </div>
716
-
717
- <div class="grid grid-cols-3 items-center gap-4">
718
- <label for="reigsterFile" class="text-sm font-medium text-gray-700 text-right">Team Datas</label>
719
- <input type="file" id="reigsterFile" name="reigsterFile"
720
- class="col-span-2 text-sm text-gray-500 file:mr-4 file:py-2 file:px-4
721
- file:rounded-md file:border-0
722
- file:text-sm file:font-semibold
723
- file:bg-blue-50 file:text-blue-700
724
- hover:file:bg-blue-100">
725
- </div>
726
- <p class="mt-1 text-xs text-gray-500">
727
- Please upload a .xlsx file with the following format: <br>
728
- <a href="/register_template_file" download class="text-blue-600 hover:underline">Download here</a>.
729
- </p>
730
- <p id="errorMessage" class="text-red-600 text-sm hidden"></p>
731
- <button type="submit"
732
- id="submitRegisterButton"
733
- 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">
734
- Submit
735
- </button>
736
- </form>
737
-
738
- </div>
739
- </div>
740
- </div>
741
- </div>
742
  <script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.js"></script>
743
  <script>
744
  document.addEventListener("DOMContentLoaded", function () {
@@ -1040,64 +988,7 @@
1040
 
1041
  <script>
1042
  document.getElementById("registerButton").addEventListener("click", function () {
1043
- document.getElementById("register-modal").classList.remove("hidden");
1044
- });
1045
-
1046
- document.querySelector("[data-modal-hide='register-modal']").addEventListener("click", function () {
1047
- document.getElementById("register-modal").classList.add("hidden");
1048
- });
1049
-
1050
- document.getElementById('registerForm').addEventListener('submit', function (e) {
1051
- e.preventDefault();
1052
-
1053
- const teamname = document.getElementById('teamname').value.trim();
1054
- const email = document.getElementById('email').value.trim();
1055
- const fileInput = document.getElementById('reigsterFile');
1056
- const file = fileInput.files[0];
1057
- const errorMessage = document.getElementById('errorMessage');
1058
- const submitBtn = document.getElementById('submitRegisterButton');
1059
-
1060
- submitBtn.disabled = true;
1061
- submitBtn.textContent = 'Submiting...';
1062
-
1063
- errorMessage.textContent = '';
1064
- errorMessage.classList.add('hidden');
1065
-
1066
- if (!teamname) {
1067
- errorMessage.textContent = 'Team name is required.';
1068
- errorMessage.classList.remove('hidden');
1069
- return;
1070
- }
1071
-
1072
- if (!email) {
1073
- errorMessage.textContent = 'Email is required.';
1074
- errorMessage.classList.remove('hidden');
1075
- return;
1076
- }
1077
-
1078
- if (!file) {
1079
- errorMessage.textContent = 'Please upload a file.';
1080
- errorMessage.classList.remove('hidden');
1081
- return;
1082
- }
1083
-
1084
- const formData = new FormData();
1085
- formData.append('teamname', teamname);
1086
- formData.append('email', email);
1087
- formData.append('file', file);
1088
- fetch('/register', { method: 'POST', body: formData })
1089
- .then(response => response.json())
1090
- .then(data => {
1091
- if (data.success) {
1092
- document.getElementById('register-modal').classList.add("hidden");
1093
- window.location.reload();
1094
- } else {
1095
- submitBtn.disabled = false;
1096
- submitBtn.textContent = 'Submit';
1097
- errorMessage.textContent = data.response || 'An error occurred.';
1098
- errorMessage.classList.remove('hidden');
1099
- }
1100
- })
1101
  });
1102
  </script>
1103
 
 
687
  </div>
688
  </div>
689
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
690
  <script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.js"></script>
691
  <script>
692
  document.addEventListener("DOMContentLoaded", function () {
 
988
 
989
  <script>
990
  document.getElementById("registerButton").addEventListener("click", function () {
991
+ window.location.href = "/register_page";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
992
  });
993
  </script>
994
 
competitions/templates/register_page.html ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Team Registration - JSON Editor</title>
6
+ <!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/json-editor@latest/dist/jsoneditor.min.css"> -->
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/spectre.css@latest/dist/spectre.min.css">
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/spectre.css@latest/dist/spectre-exp.min.css">
9
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css">
10
+
11
+
12
+ <style>
13
+ body { font-family: Arial, sans-serif; padding: 20px; }
14
+ #editor_holder { margin-bottom: 20px; }
15
+ .btn-group-custom {
16
+ display: flex;
17
+ justify-content: center;
18
+ gap: 1rem;
19
+ margin-top: 3rem;
20
+ }
21
+ </style>
22
+ </head>
23
+ <body>
24
+ <h1>Team Registration Form</h1>
25
+ <div id="editor_holder"></div>
26
+ <div class="btn-group btn-group-custom ">
27
+ <button id="submit" class="btn btn-primary">Submit</button>
28
+ <button id="cancel" class="btn btn-error">Cancel</button>
29
+ </div>
30
+ <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
31
+ </body>
32
+
33
+ <script>
34
+ const schema = {
35
+ "title": "Team Registration",
36
+ "type": "object",
37
+ "definitions": {
38
+ "student": {
39
+ "type": "object",
40
+ "id": "student",
41
+ "properties": {
42
+ "majar": {
43
+ "type": "string",
44
+ "title": "Majar",
45
+ "minLength": 1,
46
+ },
47
+ "degree": {
48
+ "type": "string",
49
+ "title": "Degree",
50
+ "enum": ["Bachelor", "Master", "PhD"]
51
+ },
52
+ "advising_professor": {
53
+ "type": "string",
54
+ "title": "Advising Professo",
55
+ "minLength": 1,
56
+ },
57
+ "grade": {
58
+ "type": "string",
59
+ "title": "Grade",
60
+ "minLength": 1,
61
+ },
62
+ "expected_graduation_year": {
63
+ "type": "string",
64
+ "title": "Expected Graduation Year",
65
+ "minLength": 1,
66
+ }
67
+ }
68
+ }
69
+ },
70
+ "properties": {
71
+ "team_name": {
72
+ "type": "string",
73
+ "title": "Team Name",
74
+ "minLength": 1,
75
+ },
76
+ "team_members": {
77
+ "type": "array",
78
+ "title": "Team Members",
79
+ "minItems": 1,
80
+ "maxItems": 4,
81
+ "format": "tabs",
82
+ "items": {
83
+ "type": "object",
84
+ "title": "Member",
85
+ "properties": {
86
+ "name": {
87
+ "type": "string",
88
+ "title": "Name",
89
+ "minLength": 1
90
+ },
91
+ "email": {
92
+ "type": "string",
93
+ "title": "Email",
94
+ "format": "email",
95
+ "minLength": 1
96
+ },
97
+ "institution": {
98
+ "type": "string",
99
+ "title": "Institution",
100
+ "minLength": 1
101
+ },
102
+ "student_info": {
103
+ "title": "Is Student",
104
+ "oneOf": [
105
+ {
106
+ "title": "no",
107
+ "type": "null"
108
+ },
109
+ {
110
+ "title": "yes",
111
+ "$ref": "#/definitions/student"
112
+ }
113
+ ]
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ const editor = new JSONEditor(document.getElementById('editor_holder'), {
122
+ schema: schema,
123
+ theme: 'spectre',
124
+ iconlib: 'fontawesome5',
125
+ disable_array_delete_all_rows: true,
126
+ disable_array_delete_last_row: true,
127
+ disable_collapse: true,
128
+ disable_edit_json: true,
129
+ disable_properties: true,
130
+ disable_array_reorder: true,
131
+ prompt_before_delete: false,
132
+ required_by_default: true,
133
+ array_controls_top: true,
134
+ remove_button_labels: true,
135
+ show_errors: "change",
136
+ });
137
+
138
+ submitButton = document.getElementById('submit');
139
+ cancelButton = document.getElementById('cancel');
140
+
141
+ submitButton.addEventListener('click', () => {
142
+ const errors = editor.validate();
143
+ editor.showValidationErrors(errors);
144
+ if (errors.length > 0) {
145
+ return;
146
+ }
147
+ const regsiterData = editor.getValue();
148
+ regsiterData.team_members = regsiterData.team_members.map(member => {
149
+ return {
150
+ name: member.name,
151
+ email: member.email,
152
+ institution: member.institution,
153
+ is_student: member.student_info !== null,
154
+ ...member.student_info
155
+ };
156
+ });
157
+
158
+ submitButton.textContent = "Submitting...";
159
+ submitButton.classList.add('disabled');
160
+ cancelButton.classList.add('disabled');
161
+ fetch('/register', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(regsiterData) })
162
+ .then(response => {
163
+ if (response.status !== 200) {
164
+ throw new Error('Network response was not ok');
165
+ }
166
+ return response.json();
167
+ })
168
+ .then(data => {
169
+ if (data.success) {
170
+ window.location.href = "/"
171
+ } else {
172
+ alert("Registration failed: " + data.response);
173
+ }
174
+ })
175
+ .finally(() => {
176
+ setTimeout(() => {
177
+ submitButton.textContent = "Submit";
178
+ submitButton.classList.remove('disabled');
179
+ cancelButton.classList.remove('disabled');
180
+ }, 2000);
181
+ })
182
+ });
183
+ cancelButton.addEventListener('click', () => {
184
+ window.location.href = "/";
185
+ });
186
+ </script>
187
+ </html>
competitions/utils.py CHANGED
@@ -303,7 +303,7 @@ class TeamFileApi:
303
 
304
  return team_metadata[team_id]
305
 
306
- def _create_team(self, user_id: str, team_name: str, team_file: Optional[io.BytesIO], email: str) -> str:
307
  with self._lock:
308
  user_team = hf_hub_download(
309
  repo_id=self.competition_id,
@@ -333,7 +333,7 @@ class TeamFileApi:
333
  "name": team_name,
334
  "members": [user_id],
335
  "leader": user_id,
336
- "email": email,
337
  }
338
 
339
  user_team_json = json.dumps(user_team, indent=4)
@@ -357,19 +357,11 @@ class TeamFileApi:
357
  repo_id=self.competition_id,
358
  repo_type="dataset",
359
  )
360
-
361
- if team_file is not None:
362
- resp = api.upload_file(
363
- path_or_fileobj=team_file,
364
- path_in_repo=f"team_datas/{team_id}.xlsx",
365
- repo_id=self.competition_id,
366
- repo_type="dataset",
367
- )
368
  return team_id
369
 
370
- def create_team(self, user_token: str, team_name: str, team_file: io.BytesIO, email: str) -> str:
371
  user_info = token_information(token=user_token)
372
- return self._create_team(user_info["id"], team_name, team_file, email)
373
 
374
  def get_team_info(self, user_token: str) -> Optional[Dict[str, Any]]:
375
  user_info = token_information(token=user_token)
 
303
 
304
  return team_metadata[team_id]
305
 
306
+ def _create_team(self, user_id: str, team_name: str, other_data: Dict[str, Any]) -> str:
307
  with self._lock:
308
  user_team = hf_hub_download(
309
  repo_id=self.competition_id,
 
333
  "name": team_name,
334
  "members": [user_id],
335
  "leader": user_id,
336
+ "other_data": other_data,
337
  }
338
 
339
  user_team_json = json.dumps(user_team, indent=4)
 
357
  repo_id=self.competition_id,
358
  repo_type="dataset",
359
  )
 
 
 
 
 
 
 
 
360
  return team_id
361
 
362
+ def create_team(self, user_token: str, team_name: str, other_data: Dict[str, Any]) -> str:
363
  user_info = token_information(token=user_token)
364
+ return self._create_team(user_info["id"], team_name, other_data)
365
 
366
  def get_team_info(self, user_token: str) -> Optional[Dict[str, Any]]:
367
  user_info = token_information(token=user_token)