| <!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>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 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="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 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 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> |
| async function loadMeta() { |
| const response = await fetch('/api/task1/dev/meta'); |
| if (!response.ok) return; |
| const payload = await response.json(); |
| document.getElementById('submission-count').textContent = String(payload.submission_count ?? 0); |
| document.getElementById('last-updated').textContent = payload.last_updated ?? 'Not evaluated yet'; |
| 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>${JSON.stringify(payload, null, 2)}</pre>`; |
| return; |
| } |
| const row = payload.leaderboard_row || null; |
| const validation = payload.validation || {}; |
| const scoreCells = row |
| ? ` |
| <div><span>Rank</span><strong>${row.rank ?? '-'}</strong></div> |
| <div><span>Accuracy</span><strong>${row.accuracy ?? '-'}</strong></div> |
| <div><span>Coverage</span><strong>${row.coverage ?? '-'}</strong></div>` |
| : ` |
| <div><span>Coverage</span><strong>${validation.coverage ?? '-'}</strong></div> |
| <div><span>Answered</span><strong>${validation.answered ?? '-'} / ${validation.total ?? '-'}</strong></div> |
| <div><span>Valid</span><strong>${validation.valid_submission ? 'yes' : 'no'}</strong></div>`; |
| panel.innerHTML = ` |
| <h3>Submission Accepted</h3> |
| <p>${payload.message}</p> |
| <div class="result-grid"> |
| <div><span>Team</span><strong>${payload.team_name}</strong></div> |
| ${scoreCells} |
| </div> |
| <p class="helper-text">Validation: unknown IDs = ${validation.unknown_ids?.length ?? 0}, missing IDs = ${validation.missing_ids?.length ?? 0}, duplicate IDs = ${validation.duplicate_ids ?? 0}, invalid predictions = ${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> |
|
|