Zhuohan's picture
Close final-test submissions
f3dac7c verified
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>__PORTAL_SUBMISSION_TITLE__</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=Source+Serif+4:wght@500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/task1/dev/static/app.css" />
</head>
<body>
<div class="page-shell narrow-shell">
<div class="site-ribbon">
<span>FinMMEval Lab 2026 · CLEF Official __PORTAL_TITLE__</span>
<a href="__OFFICIAL_SITE_URL__">Main Site</a>
</div>
<header class="topbar compact-topbar">
<div class="brand-block">
<div class="brand-kicker">Participant Submission</div>
<h1>__PORTAL_VARIANT__ Dev Upload</h1>
<p id="submission-intro">Upload one prediction file per team. A new upload from the same team replaces the older one and refreshes the official __PORTAL_VARIANT__ dev ranking.</p>
</div>
<nav class="nav-links">
<a href="__OFFICIAL_SITE_URL__">Official Site</a>
<a href="/task1/dev">Home</a>
<a id="leaderboard-link" href="/task1/dev/leaderboard">Leaderboard</a>
</nav>
</header>
<main class="form-layout">
<section class="panel">
<h2>Submit Predictions</h2>
<p class="helper-text">This page is part of the official FinMMEval Lab Task 1 __PORTAL_VARIANT__ dev workflow.</p>
<p class="helper-text">Please submit under one consistent official team name. Multiple aliases, test names, or duplicate team entries may be merged, renamed, or removed by the organizers to keep the leaderboard clean.</p>
<form id="submission-form" class="stack-form">
<label>
<span>Team Name</span>
<input type="text" id="team_name" name="team_name" placeholder="Example: MBZUAI Finance Lab" required />
</label>
<label id="contact-email-field" class="hidden">
<span>Email</span>
<input type="email" id="contact_email" name="contact_email" placeholder="name@example.com" autocomplete="email" />
</label>
<label id="submission-code-field" class="hidden">
<span>Submission Code</span>
<input type="password" id="submission_code" name="submission_code" autocomplete="off" />
</label>
<label>
<span>Prediction File</span>
<input type="file" id="prediction_file" name="prediction_file" accept=".json,.jsonl" required />
</label>
<div class="helper-text">
Required per-item format:
<code>{"id":"acct-dev-001","prediction":"B"}</code>
</div>
<div class="card-actions">
<button id="submit-button" type="submit" class="button button-primary">Submit And Refresh Leaderboard</button>
<a class="button button-secondary" href="/api/task1/template/download">Download Template</a>
</div>
</form>
</section>
<aside class="panel muted-panel">
<h3>Resources</h3>
<ul class="resource-list">
<li><a href="/api/task1/devset/download">Download Public Dev Set</a></li>
<li><a href="/api/task1/template/download">Download Submission Template</a></li>
<li><a id="resource-leaderboard-link" href="/task1/dev/leaderboard">View Live Leaderboard</a></li>
</ul>
<div class="mini-status">
<div><span>Submissions</span><strong id="submission-count">Loading...</strong></div>
<div><span>Last Refresh</span><strong id="last-updated">Loading...</strong></div>
</div>
</aside>
</main>
<section id="result-panel" class="result-panel hidden"></section>
</div>
<script>
function escapeHtml(value) {
return String(value ?? '')
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#39;');
}
async function loadMeta() {
const response = await fetch('/api/task1/dev/meta');
if (!response.ok) return;
const payload = await response.json();
const isTestMode = payload.portal_mode === 'test';
document.getElementById('submission-count').textContent = String(payload.submission_count ?? 0);
document.getElementById('last-updated').textContent = payload.last_updated ?? 'Not evaluated yet';
document.getElementById('leaderboard-link').textContent = isTestMode ? 'Submission Status' : 'Leaderboard';
document.getElementById('resource-leaderboard-link').textContent = isTestMode ? 'View Submission Status' : 'View Live Leaderboard';
document.getElementById('submit-button').textContent = isTestMode ? 'Submit Final Predictions' : 'Submit And Refresh Leaderboard';
document.getElementById('submission-intro').textContent = isTestMode
? 'Upload one final-test prediction file. Your email is used as the unique submission key; a later upload from the same email replaces the earlier active submission. Scores and ranks remain hidden.'
: 'Upload one prediction file per team. A new upload from the same team replaces the older one and refreshes the official __PORTAL_VARIANT__ dev ranking.';
if (payload.submissions_closed) {
document.getElementById('submit-button').textContent = 'Submission Window Closed';
document.getElementById('submit-button').disabled = true;
document.getElementById('submission-intro').textContent = 'The final-test submission window is closed. Please use the final leaderboards for official Task 1 results.';
document.querySelectorAll('#submission-form input, #submission-form button').forEach((item) => {
item.disabled = true;
});
}
const emailField = document.getElementById('contact-email-field');
const emailInput = document.getElementById('contact_email');
if (payload.requires_email) {
emailField.classList.remove('hidden');
emailInput.required = true;
} else {
emailField.classList.add('hidden');
emailInput.required = false;
}
const codeField = document.getElementById('submission-code-field');
const codeInput = document.getElementById('submission_code');
if (payload.requires_team_code) {
codeField.classList.remove('hidden');
codeInput.required = true;
document.getElementById('team_name').required = false;
} else {
codeField.classList.add('hidden');
codeInput.required = false;
document.getElementById('team_name').required = true;
}
}
function renderResult(payload, isError = false) {
const panel = document.getElementById('result-panel');
panel.classList.remove('hidden', 'result-success', 'result-error');
panel.classList.add(isError ? 'result-error' : 'result-success');
if (isError) {
panel.innerHTML = `<h3>Submission Failed</h3><pre>${escapeHtml(JSON.stringify(payload, null, 2))}</pre>`;
return;
}
const row = payload.leaderboard_row || null;
const validation = payload.validation || {};
const evaluation = payload.evaluation || {};
const scoreCells = row
? `
<div><span>Rank</span><strong>${escapeHtml(row.rank ?? '-')}</strong></div>
<div><span>Accuracy</span><strong>${escapeHtml(row.accuracy ?? '-')}</strong></div>
<div><span>Coverage</span><strong>${escapeHtml(row.coverage ?? '-')}</strong></div>`
: `
<div><span>Coverage</span><strong>${escapeHtml(validation.coverage ?? '-')}</strong></div>
<div><span>Answered</span><strong>${escapeHtml(validation.answered ?? '-')} / ${escapeHtml(validation.total ?? '-')}</strong></div>
<div><span>Format Valid</span><strong>${validation.valid_submission ? 'yes' : 'no'}</strong></div>
<div><span>Evaluation</span><strong>${evaluation.completed ? 'completed' : 'not completed'}</strong></div>`;
panel.innerHTML = `
<h3>Submission Accepted</h3>
<p>${escapeHtml(payload.message)}</p>
<div class="result-grid">
<div><span>Team</span><strong>${escapeHtml(payload.team_name)}</strong></div>
${scoreCells}
</div>
<p class="helper-text">Validation: unknown IDs = ${escapeHtml(validation.unknown_ids?.length ?? 0)}, missing IDs = ${escapeHtml(validation.missing_ids?.length ?? 0)}, duplicate IDs = ${escapeHtml(validation.duplicate_ids ?? 0)}, invalid predictions = ${escapeHtml(validation.invalid_prediction_count ?? 0)}</p>
`;
}
document.getElementById('submission-form').addEventListener('submit', async (event) => {
event.preventDefault();
const form = event.currentTarget;
const data = new FormData(form);
const response = await fetch('/api/task1/submissions', {
method: 'POST',
body: data,
});
const payload = await response.json();
if (!response.ok) {
renderResult(payload, true);
return;
}
renderResult(payload, false);
form.reset();
loadMeta();
});
loadMeta();
</script>
</body>
</html>