|
|
<!doctype html> |
|
|
<html> |
|
|
|
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> |
|
|
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio,line-clamp"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"> |
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.css" rel="stylesheet" /> |
|
|
<script> |
|
|
function fetchAndDisplaySubmissionInfo() { |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
fetch('/submission_info') |
|
|
.then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
|
|
|
const contentDiv = document.getElementById('content'); |
|
|
contentDiv.innerHTML = marked.parse(data.response); |
|
|
addTargetBlankToLinks(); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('There has been a problem with your fetch operation:', error); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}); |
|
|
} |
|
|
|
|
|
function fetchAndDisplaySubmissions() { |
|
|
const apiEndpoint = '/my_submissions'; |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
|
|
|
const requestOptions = { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
} |
|
|
}; |
|
|
|
|
|
fetch(apiEndpoint, requestOptions) |
|
|
.then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
const contentDiv = document.getElementById('content'); |
|
|
const teamNameDiv = `<h2>${data.response.team_name}</h2>` |
|
|
|
|
|
|
|
|
if (data.response.submissions && data.response.submissions.length > 0 && data.response.error.length == 0) { |
|
|
|
|
|
let tableHTML = teamNameDiv; |
|
|
tableHTML += '<table border="1"><tr><th width: 17%;>Datetime</th><th style="width: 40%;">Submission ID</th><th style="width: 20%;">Score</th><th style="width: 8%;">Status</th><th style="width: 15%;">Error</th></tr>'; |
|
|
|
|
|
|
|
|
data.response.submissions.forEach(submission => { |
|
|
logFileLink = submission.log_file_url === "" ? "" : `<a href="${submission.log_file_url}" target="_blank">log_file.txt</a>`; |
|
|
tableHTML += `<tr> |
|
|
<td>${submission.datetime}</td> |
|
|
<td>${submission.submission_id}</td> |
|
|
<td>${submission.score}</td> |
|
|
<td>${submission.status}</td> |
|
|
<td>${submission.error_message}<br> ${logFileLink}</td> |
|
|
</tr>`; |
|
|
}); |
|
|
|
|
|
|
|
|
tableHTML += '</table>'; |
|
|
|
|
|
contentDiv.innerHTML = marked.parse(data.response.submission_text) + tableHTML; |
|
|
} else { |
|
|
|
|
|
contentDiv.innerHTML = teamNameDiv + marked.parse(data.response.submission_text) + marked.parse(data.response.error); |
|
|
} |
|
|
document.getElementById('updateTeamNameButton').addEventListener('click', function () { |
|
|
updateTeamName(); |
|
|
}); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('There was a problem with the fetch operation:', error); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}); |
|
|
} |
|
|
|
|
|
function showSubmissionModal() { |
|
|
const modal = document.getElementById('submission-modal'); |
|
|
modal.classList.add('flex'); |
|
|
modal.classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () { |
|
|
function hideSubmissionModal() { |
|
|
const modal = document.getElementById('submission-modal'); |
|
|
modal.classList.remove('flex'); |
|
|
modal.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
function addTargetBlankToLinks() { |
|
|
const content = document.getElementById('content'); |
|
|
const links = content.getElementsByTagName('a'); |
|
|
|
|
|
for (let i = 0; i < links.length; i++) { |
|
|
if (!links[i].hasAttribute('target')) { |
|
|
links[i].setAttribute('target', '_blank'); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function fetchAndDisplayCompetitionInfo() { |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
fetch('/competition_info') |
|
|
.then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
|
|
|
const contentDiv = document.getElementById('content'); |
|
|
contentDiv.style.display = 'block'; |
|
|
contentDiv.innerHTML = marked.parse(data.response); |
|
|
addTargetBlankToLinks(); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('There has been a problem with your fetch operation:', error); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}); |
|
|
} |
|
|
|
|
|
function fetchAndDisplayDatasetInfo() { |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
fetch('/dataset_info') |
|
|
.then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
|
|
|
const contentDiv = document.getElementById('content'); |
|
|
contentDiv.innerHTML = marked.parse(data.response); |
|
|
addTargetBlankToLinks(); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('There has been a problem with your fetch operation:', error); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function fetchAndDisplayTeamInfo() { |
|
|
const apiEndpoint = '/team_info'; |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
|
|
|
const requestOptions = { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
} |
|
|
}; |
|
|
fetch(apiEndpoint, requestOptions) |
|
|
.then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
|
|
|
const contentDiv = document.getElementById('content'); |
|
|
if (data.team_exists) { |
|
|
contentHTML = "<h2>Team</h2>"; |
|
|
contentHTML += "<p>" + data.team_name + "</p>"; |
|
|
contentDiv.innerHTML = marked.parse(contentHTML); |
|
|
} else { |
|
|
contentDiv.innerHTML = marked.parse(data.response); |
|
|
} |
|
|
contentDiv.innerHTML = marked.parse(data.response); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('There has been a problem with your fetch operation:', error); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}); |
|
|
} |
|
|
|
|
|
function fetchAndDisplayRules() { |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
fetch('/rules') |
|
|
.then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
|
|
|
const contentDiv = document.getElementById('content'); |
|
|
contentDiv.innerHTML = marked.parse(data.response); |
|
|
addTargetBlankToLinks(); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('There has been a problem with your fetch operation:', error); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}); |
|
|
} |
|
|
function fetchAndDisplayLeaderboard() { |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
|
|
|
fetch('/leaderboard', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json' |
|
|
}, |
|
|
}).then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}).then(data => { |
|
|
const contentDiv = document.getElementById('content'); |
|
|
contentDiv.innerHTML = marked.parse(data.response); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}).catch(error => { |
|
|
console.error('There has been a problem with your fetch operation:', error); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
}); |
|
|
} |
|
|
|
|
|
const homeLink = document.getElementById('home'); |
|
|
const datasetLink = document.getElementById('dataset'); |
|
|
|
|
|
|
|
|
const rulesLink = document.getElementById('rules'); |
|
|
const leaderBoardLink = document.getElementById('leaderboard'); |
|
|
|
|
|
leaderBoardLink.addEventListener('click', function (event) { |
|
|
event.preventDefault(); |
|
|
fetchAndDisplayLeaderboard(); |
|
|
}); |
|
|
|
|
|
|
|
|
homeLink.addEventListener('click', function (event) { |
|
|
event.preventDefault(); |
|
|
fetchAndDisplayCompetitionInfo(); |
|
|
}); |
|
|
|
|
|
datasetLink.addEventListener('click', function (event) { |
|
|
event.preventDefault(); |
|
|
fetchAndDisplayDatasetInfo(); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rulesLink.addEventListener('click', function (event) { |
|
|
event.preventDefault(); |
|
|
fetchAndDisplayRules(); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
fetchAndDisplayCompetitionInfo(); |
|
|
|
|
|
document.querySelector('#submission-modal .cancel').addEventListener('click', function () { |
|
|
hideSubmissionModal(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
</script> |
|
|
|
|
|
<script> |
|
|
function makeApiRequest(url, callback) { |
|
|
var xhr = new XMLHttpRequest(); |
|
|
xhr.open("GET", url, true); |
|
|
xhr.onreadystatechange = function () { |
|
|
if (xhr.readyState === 4 && xhr.status === 200) { |
|
|
var response = JSON.parse(xhr.responseText); |
|
|
callback(response.response); |
|
|
} |
|
|
}; |
|
|
xhr.send(); |
|
|
} |
|
|
|
|
|
function checkOAuth() { |
|
|
var url = "/login_status"; |
|
|
const submissionInfo = document.getElementById('submission_info'); |
|
|
const mySubmissions = document.getElementById('my_submissions'); |
|
|
const newSubmission = document.getElementById('new_submission'); |
|
|
makeApiRequest(url, function ({is_login, is_admin, is_registered, is_white_team}) { |
|
|
const registerRemoveStyle = ["pointer-events-none", "text-gray-400", "cursor-not-allowed"]; |
|
|
const registerNewStyle = ["ext-gray-900", "hover:bg-gray-100"] |
|
|
if (is_login) { |
|
|
document.getElementById("loginButton").style.display = "none"; |
|
|
document.getElementById("logoutButton").style.display = "block"; |
|
|
} else { |
|
|
document.getElementById("loginButton").style.display = "block"; |
|
|
document.getElementById("logoutButton").style.display = "none"; |
|
|
} |
|
|
if (is_admin) { |
|
|
document.getElementById("admin").classList.remove("hidden"); |
|
|
} |
|
|
if (is_registered) { |
|
|
document.getElementById("updateTeamInfo").style.display = "block"; |
|
|
} |
|
|
submissionInfo.addEventListener('click', function (event) { |
|
|
event.preventDefault(); |
|
|
if (is_registered) { |
|
|
fetchAndDisplaySubmissionInfo(); |
|
|
} else { |
|
|
alert(`You need to register to access the "Submission Information."`) |
|
|
} |
|
|
}); |
|
|
mySubmissions.addEventListener('click', function (event) { |
|
|
event.preventDefault(); |
|
|
if (is_white_team) { |
|
|
fetchAndDisplaySubmissions(); |
|
|
} else { |
|
|
alert(`Access to "SUBMIT" will be granted after we manually review your registration. This process usually takes up to 24 hours.`) |
|
|
} |
|
|
}); |
|
|
newSubmission.addEventListener('click', function (event) { |
|
|
event.preventDefault(); |
|
|
if (is_white_team) { |
|
|
showSubmissionModal(); |
|
|
} else { |
|
|
alert(`Access to "SUBMIT" will be granted after we manually review your registration. This process usually takes up to 24 hours.`) |
|
|
} |
|
|
}); |
|
|
if (is_login && !is_registered) { |
|
|
document.getElementById("registerButton").style.display = "block"; |
|
|
} |
|
|
}); |
|
|
} |
|
|
window.onload = checkOAuth; |
|
|
</script> |
|
|
</head> |
|
|
|
|
|
<body class="flex h-screen"> |
|
|
|
|
|
<aside id="sidebar-multi-level-sidebar" |
|
|
class="fixed top-0 left-0 z-40 w-64 h-screen transition-transform -translate-x-full sm:translate-x-0" |
|
|
aria-label="Sidebar"> |
|
|
<div class="h-full px-3 py-4 overflow-y-auto"> |
|
|
<ul class="space-y-2 font-medium"> |
|
|
<li> |
|
|
<a href="#" id="home" |
|
|
class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 group"> |
|
|
<svg class="w-5 h-5 text-gray-500 transition duration-75 group-hover:text-gray-900" |
|
|
viewBox="0 0 22 21" xmlns="http://www.w3.org/2000/svg" fill="currentColor"> |
|
|
<path d="M1,10 L11,1 L21,10 L21,20 L1,20 Z" /> |
|
|
<path d="M6,20 L6,14 L16,14 L16,20" /> |
|
|
</svg> |
|
|
|
|
|
<span class="ms-3">Home</span> |
|
|
</a> |
|
|
</li> |
|
|
<li> |
|
|
<a href="#" id="dataset" |
|
|
class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 group"> |
|
|
<svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 group-hover:text-gray-900" |
|
|
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" |
|
|
viewBox="0 0 18 18"> |
|
|
<path |
|
|
d="M6.143 0H1.857A1.857 1.857 0 0 0 0 1.857v4.286C0 7.169.831 8 1.857 8h4.286A1.857 1.857 0 0 0 8 6.143V1.857A1.857 1.857 0 0 0 6.143 0Zm10 0h-4.286A1.857 1.857 0 0 0 10 1.857v4.286C10 7.169 10.831 8 11.857 8h4.286A1.857 1.857 0 0 0 18 6.143V1.857A1.857 1.857 0 0 0 16.143 0Zm-10 10H1.857A1.857 1.857 0 0 0 0 11.857v4.286C0 17.169.831 18 1.857 18h4.286A1.857 1.857 0 0 0 8 16.143v-4.286A1.857 1.857 0 0 0 6.143 10Zm10 0h-4.286A1.857 1.857 0 0 0 10 11.857v4.286c0 1.026.831 1.857 1.857 1.857h4.286A1.857 1.857 0 0 0 18 16.143v-4.286A1.857 1.857 0 0 0 16.143 10Z" /> |
|
|
</svg> |
|
|
<span class="flex-1 ms-3 whitespace-nowrap">Dataset</span> |
|
|
</a> |
|
|
</li> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<span id="rules"></span> |
|
|
<li> |
|
|
<a href="#" id="leaderboard" |
|
|
class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 group"> |
|
|
<svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 group-hover:text-gray-900" |
|
|
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" |
|
|
viewBox="0 0 18 21"> |
|
|
<path d="M2,4 L20,4 L20,16 L2,16 Z" /> |
|
|
<path d="M6,17 L16,17 L16,18 L6,18 Z" /> |
|
|
</svg> |
|
|
<span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">Leaderboard</span> |
|
|
</a> |
|
|
</li> |
|
|
<li> |
|
|
<button type="button" |
|
|
class="flex items-center w-full p-2 text-base text-gray-900 transition duration-75 rounded-lg group hover:bg-gray-100" |
|
|
aria-controls="submissions-dropdown" data-collapse-toggle="submissions-dropdown"> |
|
|
<svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 group-hover:text-gray-900" |
|
|
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" |
|
|
viewBox="0 0 20 20"> |
|
|
<path d="M5 5V.13a2.96 2.96 0 0 0-1.293.749L.879 3.707A2.96 2.96 0 0 0 .13 5H5Z" /> |
|
|
<path |
|
|
d="M6.737 11.061a2.961 2.961 0 0 1 .81-1.515l6.117-6.116A4.839 4.839 0 0 1 16 2.141V2a1.97 1.97 0 0 0-1.933-2H7v5a2 2 0 0 1-2 2H0v11a1.969 1.969 0 0 0 1.933 2h12.134A1.97 1.97 0 0 0 16 18v-3.093l-1.546 1.546c-.413.413-.94.695-1.513.81l-3.4.679a2.947 2.947 0 0 1-1.85-.227 2.96 2.96 0 0 1-1.635-3.257l.681-3.397Z" /> |
|
|
<path |
|
|
d="M8.961 16a.93.93 0 0 0 .189-.019l3.4-.679a.961.961 0 0 0 .49-.263l6.118-6.117a2.884 2.884 0 0 0-4.079-4.078l-6.117 6.117a.96.96 0 0 0-.263.491l-.679 3.4A.961.961 0 0 0 8.961 16Zm7.477-9.8a.958.958 0 0 1 .68-.281.961.961 0 0 1 .682 1.644l-.315.315-1.36-1.36.313-.318Zm-5.911 5.911 4.236-4.236 1.359 1.359-4.236 4.237-1.7.339.341-1.699Z" /> |
|
|
</svg> |
|
|
<span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">Submissions</span> |
|
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" |
|
|
viewBox="0 0 10 6"> |
|
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" |
|
|
d="m1 1 4 4 4-4" /> |
|
|
</svg> |
|
|
</button> |
|
|
<ul id="submissions-dropdown" class="py-2 space-y-2"> |
|
|
<li> |
|
|
<a href="#" id="submission_info" |
|
|
class="flex items-center w-full p-2 transition duration-75 rounded-lg pl-11 group ext-gray-900 hover:bg-gray-100">Submission |
|
|
information</a> |
|
|
</li> |
|
|
<li> |
|
|
<a href="#" id="my_submissions" |
|
|
class="flex items-center w-full p-2 transition duration-75 rounded-lg pl-11 group ext-gray-900 hover:bg-gray-100">My |
|
|
submissions</a> |
|
|
</li> |
|
|
<li> |
|
|
<a href="#" id="new_submission" |
|
|
class="flex items-center w-full p-2 transition duration-75 rounded-lg pl-11 group ext-gray-900 hover:bg-gray-100">New |
|
|
submission</a> |
|
|
</li> |
|
|
</ul> |
|
|
</li> |
|
|
<li> |
|
|
<a href="#" id="admin" |
|
|
class="flex items-center p-2 text-gray-900 rounded-lg hover:bg-gray-100 group hidden"> |
|
|
<svg class="flex-shrink-0 w-5 h-5 text-gray-500 transition duration-75 group-hover:text-gray-900" |
|
|
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" |
|
|
viewBox="0 0 24 24"> |
|
|
<path |
|
|
d="M12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5zm7.43-3.5c.04-.33.07-.66.07-1s-.03-.67-.07-1l2.11-1.65c.19-.15.23-.42.12-.63l-2-3.46c-.11-.21-.35-.3-.57-.24l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.57 2.18 14.3 2 14 2h-4c-.3 0-.57.18-.64.45L8.98 5.1c-.61.25-1.17.58-1.69.98l-2.49-1c-.22-.06-.46.03-.57.24l-2 3.46c-.11.21-.07.48.12.63l2.11 1.65c-.04.33-.07.66-.07 1s.03.67.07 1L2.46 14.1c-.19.15-.23.42-.12.63l2 3.46c.11.21.35.3.57.24l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.07.27.34.45.64.45h4c.3 0 .57-.18.64-.45l.38-2.65c.61-.25 1.17-.58 1.69-.98l2.49 1c.22.06.46-.03.57-.24l2-3.46c.11-.21.07-.48-.12-.63l-2.11-1.65zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" /> |
|
|
</svg> |
|
|
<span class="flex-1 ms-3 whitespace-nowrap">Admin</span> |
|
|
</a> |
|
|
</li> |
|
|
<li id="registerButton" style="display: none;"> |
|
|
<a href="#" |
|
|
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"> |
|
|
Register |
|
|
</a> |
|
|
</li> |
|
|
<li id="loginButton" style="display: none;"> |
|
|
<a href="/login/huggingface" |
|
|
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"> |
|
|
Login with Hugging Face |
|
|
</a> |
|
|
</li> |
|
|
<li id="updateTeamInfo" style="display: none;"> |
|
|
<a href="/update_team_info_page" |
|
|
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"> |
|
|
Update Team Info |
|
|
</a> |
|
|
</li> |
|
|
<li id="logoutButton" style="display: none;"> |
|
|
<a href="/logout" |
|
|
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"> |
|
|
Logout |
|
|
</a> |
|
|
</li> |
|
|
</ul> |
|
|
|
|
|
<footer> |
|
|
<div class="w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-between"> |
|
|
<span class="text-sm text-gray-500 sm:text-center">Powered by <a |
|
|
href="https://github.com/huggingface/competitions" target="_blank" |
|
|
class="hover:underline">Hugging Face |
|
|
Competitions</a> |
|
|
</span> |
|
|
</div> |
|
|
<div class="text-center"> |
|
|
<span class="text-xs text-gray-400">{{version}} |
|
|
</span> |
|
|
</div> |
|
|
</footer> |
|
|
</div> |
|
|
</aside> |
|
|
<div class="p-1 sm:ml-64 overflow-x-hidden"> |
|
|
<img src={{logo}} alt="Competition logo"> |
|
|
<hr class="mt-3 mb-2"> |
|
|
<div id="articleLoadingSpinner" role="status" |
|
|
class="hidden absolute -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2"> |
|
|
<div class="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div> |
|
|
<span class="sr-only">Loading...</span> |
|
|
</div> |
|
|
<article class="prose w-full mx-auto max-w-screen-xl p-4 md:flex md:items-center md:justify-between" |
|
|
id="content"> |
|
|
</article> |
|
|
</div> |
|
|
<div id="submission-modal" tabindex="-1" |
|
|
class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full"> |
|
|
<div id="loadingSpinner" role="status" |
|
|
class="hidden absolute -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2"> |
|
|
<div class="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div> |
|
|
<span class="sr-only">Loading...</span> |
|
|
</div> |
|
|
<div class="form-container max-w-5xl mx-auto mt-3 p-6 shadow-2xl bg-white"> |
|
|
<p class="text-lg font-medium text-gray-900">New Submission</p> |
|
|
<form action="#" method="post" class="gap-2" enctype="multipart/form-data"> |
|
|
{% if competition_type == 'generic' %} |
|
|
<div class="form-group"> |
|
|
<label class="block mb-2 text-sm font-medium text-gray-900" for="submission_file">Upload |
|
|
file</label> |
|
|
<input |
|
|
class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 focus:outline-none " |
|
|
id="submission_file" type="file" name="submission_file"> |
|
|
</div> |
|
|
{% endif %} |
|
|
{% if competition_type == 'script' %} |
|
|
<div class="form-group"> |
|
|
<label for="hub_model" class="text-sm font-medium text-gray-700">Hub Model |
|
|
</label> |
|
|
<input type="text" name="hub_model" id="hub_model" |
|
|
class="mt-1 block w-full border border-gray-300 px-3 py-1.5 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" |
|
|
placeholder="username/my-model"> |
|
|
</div> |
|
|
{% endif %} |
|
|
<div class="form-group mt-2"> |
|
|
<label for="submission_comment" class="text-sm font-medium text-gray-700">Submission description |
|
|
(optional) |
|
|
</label> |
|
|
<textarea id="submission_comment" name="submission_comment" rows="5" |
|
|
class="p-2.5 w-full text-sm text-gray-900" placeholder=" "></textarea> |
|
|
</div> |
|
|
<div class="form-actions mt-6"> |
|
|
<button data-modal-hide="submission-modal" type="button" |
|
|
class="confirm text-white bg-green-600 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center me-2"> |
|
|
Submit |
|
|
</button> |
|
|
<button data-modal-hide="submission-modal" type="button" |
|
|
class="cancel text-white bg-red-600 hover:bg-red-100 focus:ring-4 focus:outline-none focus:ring-red-200 rounded-lg border border-red-200 text-sm font-medium px-5 py-2.5 hover:text-red-900 focus:z-10">Cancel</button> |
|
|
</div> |
|
|
</form> |
|
|
<hr class="mt-3"> |
|
|
<div id="error-message" style="color: red;"></div> |
|
|
<div id="success-message" style="color: green;"></div> |
|
|
</div> |
|
|
</div> |
|
|
<div id="admin-modal" tabindex="-1" |
|
|
class="hidden fixed inset-0 z-40 flex items-center justify-center w-full h-full bg-black bg-opacity-50"> |
|
|
<div id="adminLoadingSpinner" role="status" |
|
|
class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"> |
|
|
<div class="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div> |
|
|
<span class="sr-only">Loading...</span> |
|
|
</div> |
|
|
<div class="relative w-full max-w-5xl p-4"> |
|
|
<div class="relative bg-white rounded-lg shadow-2xl"> |
|
|
<button type="button" |
|
|
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" |
|
|
data-modal-hide="admin-modal"> |
|
|
<svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" |
|
|
viewBox="0 0 14 14"> |
|
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" |
|
|
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" /> |
|
|
</svg> |
|
|
<span class="sr-only">Close</span> |
|
|
</button> |
|
|
<div class="p-6 md:p-8 text-center"> |
|
|
<h3 class="mb-5 text-lg font-medium text-gray-900">Admin</h3> |
|
|
<div class="tabs"> |
|
|
<ul class="flex border-b"> |
|
|
<li class="mr-1"> |
|
|
<a class="tab bg-white inline-block py-2 px-4 text-blue-500 hover:text-blue-800 font-semibold" |
|
|
href="#config">Config</a> |
|
|
</li> |
|
|
<li class="mr-1"> |
|
|
<a class="tab bg-white inline-block py-2 px-4 text-blue-500 hover:text-blue-800 font-semibold" |
|
|
href="#competition-desc">Competition Desc</a> |
|
|
</li> |
|
|
<li class="mr-1"> |
|
|
<a class="tab bg-white inline-block py-2 px-4 text-blue-500 hover:text-blue-800 font-semibold" |
|
|
href="#dataset-desc">Dataset Desc</a> |
|
|
</li> |
|
|
<li class="mr-1"> |
|
|
<a class="tab bg-white inline-block py-2 px-4 text-blue-500 hover:text-blue-800 font-semibold" |
|
|
href="#submission-desc">Submission Desc</a> |
|
|
</li> |
|
|
<li class="mr-1"> |
|
|
<a class="tab bg-white inline-block py-2 px-4 text-blue-500 hover:text-blue-800 font-semibold" |
|
|
href="#rules-desc">Rules</a> |
|
|
</li> |
|
|
</ul> |
|
|
</div> |
|
|
<div id="tab-contents" |
|
|
class="text-xs font-normal text-left overflow-y-auto max-h-[calc(100vh-400px)] border-t border-gray-200 pt-4"> |
|
|
<div id="config"> |
|
|
<textarea id="config-textarea" class="w-full h-64 p-2 border rounded">Loading..</textarea> |
|
|
<p class="text-xs text-gray-500">Note: The config should be a valid JSON object. To learn |
|
|
details about entries, click <a |
|
|
href="https://huggingface.co/docs/competitions/competition_repo#confjson" |
|
|
target="_blank">here</a>. |
|
|
</p> |
|
|
</div> |
|
|
<div id="competition-desc" class="hidden"> |
|
|
<textarea id="competition-desc-textarea" |
|
|
class="w-full h-64 p-2 border rounded">Loading..</textarea> |
|
|
</div> |
|
|
<div id="dataset-desc" class="hidden"> |
|
|
<textarea id="dataset-desc-textarea" |
|
|
class="w-full h-64 p-2 border rounded">Loading..</textarea> |
|
|
</div> |
|
|
<div id="submission-desc" class="hidden"> |
|
|
<textarea id="submission-desc-textarea" |
|
|
class="w-full h-64 p-2 border rounded">Loading..</textarea> |
|
|
</div> |
|
|
<div id="rules-desc" class="hidden"> |
|
|
<textarea id="rules-desc-textarea" |
|
|
class="w-full h-64 p-2 border rounded">Loading..</textarea> |
|
|
</div> |
|
|
</div> |
|
|
<button id="save-button" class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-700"> |
|
|
Save |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.1/flowbite.min.js"></script> |
|
|
<script> |
|
|
document.addEventListener("DOMContentLoaded", function () { |
|
|
const content = document.getElementById('content'); |
|
|
const links = content.getElementsByTagName('a'); |
|
|
|
|
|
for (let i = 0; i < links.length; i++) { |
|
|
if (!links[i].hasAttribute('target')) { |
|
|
links[i].setAttribute('target', '_blank'); |
|
|
} |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
|
|
|
<script> |
|
|
document.addEventListener("DOMContentLoaded", function () { |
|
|
document.querySelectorAll('.tabs a').forEach(tab => { |
|
|
tab.addEventListener('click', event => { |
|
|
event.preventDefault(); |
|
|
document.querySelectorAll('.tabs a').forEach(t => t.classList.remove('active')); |
|
|
tab.classList.add('active'); |
|
|
|
|
|
document.querySelectorAll('#tab-contents > div').forEach(content => content.classList.add('hidden')); |
|
|
const selectedTab = document.querySelector(tab.getAttribute('href')); |
|
|
selectedTab.classList.remove('hidden'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
async function fetchAdminCompInfo() { |
|
|
const adminLoadingSpinner = document.getElementById('adminLoadingSpinner'); |
|
|
adminLoadingSpinner.classList.remove('hidden'); |
|
|
try { |
|
|
const response = await fetch("/admin/comp_info", { |
|
|
method: "POST", |
|
|
headers: { |
|
|
"Content-Type": "application/json" |
|
|
} |
|
|
}); |
|
|
const data = await response.json(); |
|
|
if (response.ok) { |
|
|
populateAdminModal(data.response); |
|
|
} else { |
|
|
alert(data.response || "Failed to fetch competition info"); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error("Error fetching admin competition info:", error); |
|
|
alert("An error occurred while fetching competition info."); |
|
|
} finally { |
|
|
adminLoadingSpinner.classList.add('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
function populateAdminModal(data) { |
|
|
document.getElementById("config-textarea").value = JSON.stringify(data.config, null, 2); |
|
|
document.getElementById("competition-desc-textarea").value = data.markdowns["competition_desc"] || ""; |
|
|
document.getElementById("dataset-desc-textarea").value = data.markdowns["dataset_desc"] || ""; |
|
|
document.getElementById("submission-desc-textarea").value = data.markdowns["submission_desc"] || ""; |
|
|
document.getElementById("rules-desc-textarea").value = data.markdowns["rules"] || "No rules available."; |
|
|
} |
|
|
|
|
|
document.querySelectorAll(".tab").forEach(tab => { |
|
|
tab.addEventListener("click", function (event) { |
|
|
event.preventDefault(); |
|
|
const targetId = this.getAttribute("href").substring(1); |
|
|
|
|
|
document.querySelectorAll("#tab-contents > div").forEach(content => { |
|
|
content.classList.add("hidden"); |
|
|
}); |
|
|
document.getElementById(targetId).classList.remove("hidden"); |
|
|
|
|
|
document.querySelectorAll(".tab").forEach(t => { |
|
|
t.classList.remove("text-blue-800"); |
|
|
t.classList.add("text-blue-500"); |
|
|
}); |
|
|
this.classList.remove("text-blue-500"); |
|
|
this.classList.add("text-blue-800"); |
|
|
}); |
|
|
}); |
|
|
|
|
|
document.getElementById("admin").addEventListener("click", function () { |
|
|
document.getElementById("admin-modal").classList.remove("hidden"); |
|
|
fetchAdminCompInfo(); |
|
|
}); |
|
|
|
|
|
document.querySelector("[data-modal-hide='admin-modal']").addEventListener("click", function () { |
|
|
document.getElementById("admin-modal").classList.add("hidden"); |
|
|
}); |
|
|
|
|
|
document.getElementById("save-button").addEventListener("click", async function () { |
|
|
const adminLoadingSpinner = document.getElementById('adminLoadingSpinner'); |
|
|
adminLoadingSpinner.classList.remove('hidden'); |
|
|
|
|
|
const config = document.getElementById("config-textarea").value; |
|
|
const competitionDesc = document.getElementById("competition-desc-textarea").value; |
|
|
const datasetDesc = document.getElementById("dataset-desc-textarea").value; |
|
|
const submissionDesc = document.getElementById("submission-desc-textarea").value; |
|
|
const rulesDesc = document.getElementById("rules-desc-textarea").value; |
|
|
|
|
|
const data = { |
|
|
config: JSON.parse(config), |
|
|
markdowns: { |
|
|
competition_desc: competitionDesc, |
|
|
dataset_desc: datasetDesc, |
|
|
submission_desc: submissionDesc, |
|
|
rules: rulesDesc |
|
|
} |
|
|
}; |
|
|
|
|
|
try { |
|
|
const response = await fetch("/admin/update_comp_info", { |
|
|
method: "POST", |
|
|
headers: { |
|
|
"Content-Type": "application/json" |
|
|
}, |
|
|
body: JSON.stringify(data) |
|
|
}); |
|
|
const result = await response.json(); |
|
|
if (response.ok) { |
|
|
alert(result.response || "Successfully updated competition info"); |
|
|
} else { |
|
|
alert(result.response || "Failed to update competition info"); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error("Error updating competition info:", error); |
|
|
alert("An error occurred while updating competition info."); |
|
|
} finally { |
|
|
adminLoadingSpinner.classList.add('hidden'); |
|
|
} |
|
|
}); |
|
|
|
|
|
}); |
|
|
</script> |
|
|
|
|
|
<script> |
|
|
document.addEventListener('DOMContentLoaded', function () { |
|
|
document.querySelector('.confirm').addEventListener('click', function (event) { |
|
|
event.preventDefault(); |
|
|
document.getElementById('error-message').textContent = ''; |
|
|
document.getElementById('success-message').textContent = ''; |
|
|
const loadingSpinner = document.getElementById('loadingSpinner'); |
|
|
loadingSpinner.classList.remove('hidden'); |
|
|
|
|
|
var formData = new FormData(); |
|
|
var competitionType = '{{ competition_type }}'; |
|
|
|
|
|
if (competitionType === 'generic') { |
|
|
var submissionFile = document.getElementById('submission_file').files[0]; |
|
|
formData.append('submission_file', submissionFile); |
|
|
formData.append('hub_model', 'None'); |
|
|
} else if (competitionType === 'script') { |
|
|
var hubModel = document.getElementById('hub_model').value; |
|
|
if (!hubModel) { |
|
|
alert('Hub model is required.'); |
|
|
return; |
|
|
} |
|
|
formData.append('hub_model', hubModel); |
|
|
} else { |
|
|
alert('Invalid competition type.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
var submissionComment = document.getElementById('submission_comment').value; |
|
|
formData.append('submission_comment', submissionComment); |
|
|
|
|
|
fetch('/new_submission', { |
|
|
method: 'POST', |
|
|
body: formData |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
loadingSpinner.classList.add('hidden'); |
|
|
document.getElementById('success-message').textContent = data.response; |
|
|
|
|
|
}) |
|
|
.catch((error) => { |
|
|
console.error('Error:', error); |
|
|
loadingSpinner.classList.add('hidden'); |
|
|
document.getElementById('error-message').textContent = error; |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
|
|
|
<script> |
|
|
function updateSelectedSubmissions() { |
|
|
const selectedSubmissions = document.querySelectorAll('input[name="selectedSubmissions"]:checked'); |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
let selectedSubmissionIds = []; |
|
|
selectedSubmissions.forEach((submission) => { |
|
|
selectedSubmissionIds.push(submission.value); |
|
|
}); |
|
|
|
|
|
const updateEndpoint = '/update_selected_submissions'; |
|
|
const requestOptions = { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
"submission_ids": selectedSubmissionIds.join(',') |
|
|
}) |
|
|
}; |
|
|
|
|
|
fetch(updateEndpoint, requestOptions) |
|
|
.then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
|
|
|
console.log('Update successful'); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
} else { |
|
|
|
|
|
console.log('Update failed'); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
alert(data.error); |
|
|
} |
|
|
|
|
|
fetchAndDisplaySubmissions(); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('There was a problem with the fetch operation for updating:', error); |
|
|
}); |
|
|
} |
|
|
</script> |
|
|
|
|
|
<script> |
|
|
function updateTeamName() { |
|
|
const teamName = document.getElementById('team_name').value; |
|
|
const articleLoadingSpinner = document.getElementById('articleLoadingSpinner'); |
|
|
articleLoadingSpinner.classList.remove('hidden'); |
|
|
|
|
|
const updateEndpoint = '/update_team_name'; |
|
|
const requestOptions = { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
"new_team_name": teamName |
|
|
}) |
|
|
}; |
|
|
|
|
|
fetch(updateEndpoint, requestOptions) |
|
|
.then(response => { |
|
|
if (!response.ok) { |
|
|
throw new Error('Network response was not ok'); |
|
|
} |
|
|
return response.json(); |
|
|
}) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
|
|
|
console.log('Update successful'); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
} else { |
|
|
|
|
|
console.log('Update failed'); |
|
|
articleLoadingSpinner.classList.add('hidden'); |
|
|
alert(data.error); |
|
|
} |
|
|
|
|
|
fetchAndDisplaySubmissions(); |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('There was a problem with the fetch operation for updating:', error); |
|
|
}); |
|
|
} |
|
|
</script> |
|
|
|
|
|
<script> |
|
|
function showAdminModal() { |
|
|
const modal = document.getElementById('admin-modal'); |
|
|
modal.classList.add('flex'); |
|
|
modal.classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
function hideAdminModal() { |
|
|
const modal = document.getElementById('admin-modal'); |
|
|
modal.classList.remove('flex'); |
|
|
modal.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
document.querySelector('#admin').addEventListener('click', function () { |
|
|
showAdminModal(); |
|
|
}); |
|
|
|
|
|
document.querySelector('[data-modal-hide="admin-modal"]').addEventListener('click', function () { |
|
|
hideAdminModal(); |
|
|
}); |
|
|
</script> |
|
|
|
|
|
|
|
|
<script> |
|
|
document.getElementById("registerButton").addEventListener("click", function () { |
|
|
window.location.href = "/register_page"; |
|
|
}); |
|
|
</script> |
|
|
|
|
|
</html> |