Libra-1995 commited on
Commit
6414083
·
1 Parent(s): e13004b

feat: support update team info

Browse files
competitions/app.py CHANGED
@@ -4,6 +4,7 @@ import threading
4
  import time
5
  import io
6
  import traceback
 
7
  from typing import Dict, Any, List, Optional
8
  from urllib.parse import urlencode
9
 
@@ -520,8 +521,6 @@ def register(
520
  if team_info is not None:
521
  return {"success": False, "response": "You have already registered your team."}
522
 
523
-
524
-
525
  utils.team_file_api.create_team(
526
  user_token,
527
  request.team_name,
@@ -531,6 +530,23 @@ def register(
531
  return {"success": True, "response": "Team created successfully."}
532
 
533
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  @app.get("/error_log")
535
  def get_error_log(token: str = Query(..., description="Token to access the error log file.")):
536
  try:
@@ -559,3 +575,24 @@ def register_page(request: Request):
559
  "version": __version__,
560
  }
561
  return templates.TemplateResponse("register_page.html", context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import time
5
  import io
6
  import traceback
7
+ import json
8
  from typing import Dict, Any, List, Optional
9
  from urllib.parse import urlencode
10
 
 
521
  if team_info is not None:
522
  return {"success": False, "response": "You have already registered your team."}
523
 
 
 
524
  utils.team_file_api.create_team(
525
  user_token,
526
  request.team_name,
 
530
  return {"success": True, "response": "Team created successfully."}
531
 
532
 
533
+ @app.post("/update_team_info")
534
+ def update_team_info(
535
+ request: RegisterRequest,
536
+ user_token: str = Depends(utils.user_authentication)
537
+ ):
538
+ if user_token is None:
539
+ return {"success": False, "response": "Please login."}
540
+
541
+ utils.team_file_api.update_team(
542
+ user_token,
543
+ request.team_name,
544
+ request.model_dump()
545
+ )
546
+
547
+ return {"success": True, "response": "Update team info success."}
548
+
549
+
550
  @app.get("/error_log")
551
  def get_error_log(token: str = Query(..., description="Token to access the error log file.")):
552
  try:
 
575
  "version": __version__,
576
  }
577
  return templates.TemplateResponse("register_page.html", context)
578
+
579
+
580
+ @app.get("/update_team_info_page", response_class=HTMLResponse)
581
+ def update_team_info_page(request: Request, user_token: str = Depends(utils.user_authentication)):
582
+ """
583
+ This function is used to render the update team info HTML page.
584
+ """
585
+ if user_token is None:
586
+ return HTTPException(status_code=403, detail="Please login to access this page.")
587
+
588
+ team_info = utils.team_file_api.get_team_info(user_token)
589
+ if team_info is None:
590
+ return HTTPException(status_code=403, detail="You have not registered your team yet.")
591
+
592
+ print(team_info)
593
+ context = {
594
+ "request": request,
595
+ "team_info_json_string": json.dumps(team_info),
596
+ "version": __version__,
597
+ }
598
+ return templates.TemplateResponse("update_team_info_page.html", context)
competitions/templates/index.html CHANGED
@@ -53,11 +53,7 @@
53
  })
54
  .then(data => {
55
  const contentDiv = document.getElementById('content');
56
- const teamNameDiv = `
57
- <div class="flex items-center">
58
- <input type="text" name="team_name" id="team_name" class="mt-1 mb-1 block me-2" value="${data.response.team_name}">
59
- <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>
60
- </div>`;
61
  // console.log(data.response.submissions);
62
  // contentDiv.innerHTML = marked.parse(data.response.submission_text) + data.response.submissions;
63
  if (data.response.submissions && data.response.submissions.length > 0 && data.response.error.length == 0) {
@@ -370,6 +366,9 @@
370
  if (is_admin) {
371
  document.getElementById("admin").classList.remove("hidden");
372
  }
 
 
 
373
  submissionInfo.addEventListener('click', function (event) {
374
  event.preventDefault(); // Prevent the default link behavior
375
  if (is_registered) {
@@ -525,6 +524,12 @@
525
  Login with Hugging Face
526
  </a>
527
  </li>
 
 
 
 
 
 
528
  <li id="logoutButton" style="display: none;">
529
  <a href="/logout"
530
  class="flex justify-center items-center bg-red-400 hover:bg-red-600 text-white text-center font-bold py-2 px-4 rounded transition duration-200 ease-in-out">
 
53
  })
54
  .then(data => {
55
  const contentDiv = document.getElementById('content');
56
+ const teamNameDiv = `<h2>${data.response.team_name}</h2>`
 
 
 
 
57
  // console.log(data.response.submissions);
58
  // contentDiv.innerHTML = marked.parse(data.response.submission_text) + data.response.submissions;
59
  if (data.response.submissions && data.response.submissions.length > 0 && data.response.error.length == 0) {
 
366
  if (is_admin) {
367
  document.getElementById("admin").classList.remove("hidden");
368
  }
369
+ if (is_registered) {
370
+ document.getElementById("updateTeamInfo").style.display = "block";
371
+ }
372
  submissionInfo.addEventListener('click', function (event) {
373
  event.preventDefault(); // Prevent the default link behavior
374
  if (is_registered) {
 
524
  Login with Hugging Face
525
  </a>
526
  </li>
527
+ <li id="updateTeamInfo" style="display: none;">
528
+ <a href="/update_team_info_page"
529
+ class="flex justify-center items-center bg-green-600 hover:bg-green-800 text-white text-center font-bold py-2 px-4 rounded transition duration-200 ease-in-out">
530
+ Update Team Info
531
+ </a>
532
+ </li>
533
  <li id="logoutButton" style="display: none;">
534
  <a href="/logout"
535
  class="flex justify-center items-center bg-red-400 hover:bg-red-600 text-white text-center font-bold py-2 px-4 rounded transition duration-200 ease-in-out">
competitions/templates/update_team_info_page.html ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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": `All the information you provide in the form must be accurate; otherwise, your registration will be rejected. If you are a student participant, please check the "Is Student" box and fill in the relevant information.`,
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": 12,
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 teamInfo = JSON.parse('{{ team_info_json_string|safe }}')["other_data"];
122
+ teamInfo.team_members = teamInfo.team_members.map(member => {
123
+ const newMemberInfo = {
124
+ name: member.name,
125
+ email: member.email,
126
+ institution: member.institution
127
+ }
128
+ if (member.is_student) {
129
+ newMemberInfo.student_info = {
130
+ majar: member.majar,
131
+ degree: member.degree,
132
+ advising_professor: member.advising_professor,
133
+ grade: member.grade,
134
+ expected_graduation_year: member.expected_graduation_year
135
+ };
136
+ } else {
137
+ newMemberInfo.student_info = null;
138
+ }
139
+ return newMemberInfo;
140
+ });
141
+
142
+ const editor = new JSONEditor(document.getElementById('editor_holder'), {
143
+ schema: schema,
144
+ theme: 'spectre',
145
+ iconlib: 'fontawesome5',
146
+ disable_array_delete_all_rows: true,
147
+ disable_array_delete_last_row: true,
148
+ disable_collapse: true,
149
+ disable_edit_json: true,
150
+ disable_properties: true,
151
+ disable_array_reorder: true,
152
+ prompt_before_delete: false,
153
+ required_by_default: true,
154
+ array_controls_top: true,
155
+ remove_button_labels: true,
156
+ show_errors: "change",
157
+ startval: teamInfo,
158
+ });
159
+
160
+ submitButton = document.getElementById('submit');
161
+ cancelButton = document.getElementById('cancel');
162
+
163
+ submitButton.addEventListener('click', () => {
164
+ const errors = editor.validate();
165
+ editor.showValidationErrors(errors);
166
+ if (errors.length > 0) {
167
+ return;
168
+ }
169
+ const regsiterData = editor.getValue();
170
+ regsiterData.team_members = regsiterData.team_members.map(member => {
171
+ return {
172
+ name: member.name,
173
+ email: member.email,
174
+ institution: member.institution,
175
+ is_student: member.student_info !== null,
176
+ ...member.student_info
177
+ };
178
+ });
179
+
180
+ submitButton.textContent = "Submitting...";
181
+ submitButton.classList.add('disabled');
182
+ cancelButton.classList.add('disabled');
183
+ fetch('/update_team_info', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(regsiterData) })
184
+ .then(response => {
185
+ if (response.status !== 200) {
186
+ throw new Error('Network response was not ok');
187
+ }
188
+ return response.json();
189
+ })
190
+ .then(data => {
191
+ if (data.success) {
192
+ window.location.href = "/"
193
+ } else {
194
+ alert("Registration failed: " + data.response);
195
+ }
196
+ })
197
+ .finally(() => {
198
+ setTimeout(() => {
199
+ submitButton.textContent = "Submit";
200
+ submitButton.classList.remove('disabled');
201
+ cancelButton.classList.remove('disabled');
202
+ }, 2000);
203
+ })
204
+ });
205
+ cancelButton.addEventListener('click', () => {
206
+ window.location.href = "/";
207
+ });
208
+ </script>
209
+ </html>
competitions/utils.py CHANGED
@@ -365,6 +365,40 @@ class TeamFileApi:
365
  user_info = token_information(token=user_token)
366
  return self._create_team(user_info["id"], team_name, other_data)
367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  def get_team_info(self, user_token: str) -> Optional[Dict[str, Any]]:
369
  user_info = token_information(token=user_token)
370
  return self._get_team_info(user_info["id"])
 
365
  user_info = token_information(token=user_token)
366
  return self._create_team(user_info["id"], team_name, other_data)
367
 
368
+ def update_team(self, user_token: str, team_name: str, other_data: Dict[str, Any]) -> str:
369
+ user_info = token_information(token=user_token)
370
+ user_id = user_info["id"]
371
+ team_info = self._get_team_info(user_id)
372
+
373
+ with self._lock:
374
+ team_metadata = hf_hub_download(
375
+ repo_id=self.competition_id,
376
+ filename="teams.json",
377
+ token=self.hf_token,
378
+ repo_type="dataset",
379
+ )
380
+ with open(team_metadata, "r", encoding="utf-8") as f:
381
+ team_metadata = json.load(f)
382
+
383
+ team_id = team_info["id"]
384
+ team_detail = team_metadata[team_id]
385
+ team_metadata[team_id] = {
386
+ **team_detail,
387
+ "name": team_name,
388
+ "other_data": other_data,
389
+ }
390
+
391
+ team_metadata_json = json.dumps(team_metadata, indent=4)
392
+ team_metadata_json_bytes = team_metadata_json.encode("utf-8")
393
+ team_metadata_json_buffer = io.BytesIO(team_metadata_json_bytes)
394
+ api = HfApi(token=self.hf_token)
395
+ api.upload_file(
396
+ path_or_fileobj=team_metadata_json_buffer,
397
+ path_in_repo="teams.json",
398
+ repo_id=self.competition_id,
399
+ repo_type="dataset",
400
+ )
401
+
402
  def get_team_info(self, user_token: str) -> Optional[Dict[str, Any]]:
403
  user_info = token_information(token=user_token)
404
  return self._get_team_info(user_info["id"])