Spaces:
Running
Running
Queue Order
Browse files- app.py +4 -4
- frontend_interaction_browser_regression.py +12 -0
- frontend_interaction_regression.py +7 -1
- queue_position_regression.py +194 -0
- queue_refresh_regression.py +38 -2
- src/display/academic.py +2 -0
- src/display/table_layout.py +1 -1
- src/leaderboard/queue_position.py +66 -0
- src/leaderboard/read_evals.py +9 -2
- src/submission/summary.py +2 -1
app.py
CHANGED
|
@@ -252,7 +252,7 @@ def _dataframe(
|
|
| 252 |
interactive=False,
|
| 253 |
wrap=False,
|
| 254 |
line_breaks=False,
|
| 255 |
-
column_widths=[column_width(column) for column in value.columns],
|
| 256 |
max_height=max_height,
|
| 257 |
elem_classes=[*classes, "data-table"],
|
| 258 |
)
|
|
@@ -1410,11 +1410,11 @@ with gr.Blocks(
|
|
| 1410 |
with gr.TabItem(group["tab"], id=index, elem_id=f"tab-{group['slug']}", **TAB_RENDER_OPTIONS):
|
| 1411 |
with gr.Column(elem_id=f"task-page-{group['slug']}", elem_classes="task-page-shell"):
|
| 1412 |
track_tabs, boards, task_selector = _render_subtasks(group["tasks"], group["title"])
|
| 1413 |
-
with gr.Row(elem_classes='
|
| 1414 |
evaluator_status = gr.Markdown('Evaluator Status: Unavailable', visible=False, elem_classes='markdown-text', elem_id=f'evaluator-status-{group["slug"]}')
|
| 1415 |
-
status_refresh = gr.Button('↻ Refresh', size='sm', scale=0, min_width=96, elem_classes='board-refresh', elem_id=f'evaluator-refresh-{group["slug"]}')
|
| 1416 |
status_refresh_events.extend([(demo.load, evaluator_status), (status_refresh.click, evaluator_status)])
|
| 1417 |
-
with gr.Accordion(
|
| 1418 |
'Submission Center', open=False,
|
| 1419 |
elem_classes='submission-panel utility-panel', elem_id=f'submit-{group["slug"]}',
|
| 1420 |
):
|
|
|
|
| 252 |
interactive=False,
|
| 253 |
wrap=False,
|
| 254 |
line_breaks=False,
|
| 255 |
+
column_widths=[164 if column == "Status" and "queue-table" in classes else column_width(column) for column in value.columns],
|
| 256 |
max_height=max_height,
|
| 257 |
elem_classes=[*classes, "data-table"],
|
| 258 |
)
|
|
|
|
| 1410 |
with gr.TabItem(group["tab"], id=index, elem_id=f"tab-{group['slug']}", **TAB_RENDER_OPTIONS):
|
| 1411 |
with gr.Column(elem_id=f"task-page-{group['slug']}", elem_classes="task-page-shell"):
|
| 1412 |
track_tabs, boards, task_selector = _render_subtasks(group["tasks"], group["title"])
|
| 1413 |
+
with gr.Row(elem_classes='submission-center-shell') as submission_header:
|
| 1414 |
evaluator_status = gr.Markdown('Evaluator Status: Unavailable', visible=False, elem_classes='markdown-text', elem_id=f'evaluator-status-{group["slug"]}')
|
| 1415 |
+
status_refresh = gr.Button('↻ Refresh', visible=False, size='sm', scale=0, min_width=96, elem_classes='board-refresh', elem_id=f'evaluator-refresh-{group["slug"]}')
|
| 1416 |
status_refresh_events.extend([(demo.load, evaluator_status), (status_refresh.click, evaluator_status)])
|
| 1417 |
+
with submission_header, gr.Accordion(
|
| 1418 |
'Submission Center', open=False,
|
| 1419 |
elem_classes='submission-panel utility-panel', elem_id=f'submit-{group["slug"]}',
|
| 1420 |
):
|
frontend_interaction_browser_regression.py
CHANGED
|
@@ -81,6 +81,18 @@ async def browser_check(port, server_port, api, runtime, root):
|
|
| 81 |
|
| 82 |
await command('Page.navigate', dict(url=f'http://127.0.0.1:{server_port}/ui/login/huggingface?_target_url=/ui/'))
|
| 83 |
await wait("document.body.textContent.includes('audit-user')")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
await evaluate("document.querySelector('.submission-panel > button').classList.contains('open') || document.querySelector('.submission-panel > button').click()")
|
| 85 |
await wait("Array.from(document.querySelectorAll('.submission-panel .utility-tabs [role=tab]')).some(el=>el.textContent.trim()==='History & Reports')")
|
| 86 |
await click("Array.from(document.querySelectorAll('.submission-panel .utility-tabs [role=tab]')).find(el=>el.textContent.trim()==='History & Reports')")
|
|
|
|
| 81 |
|
| 82 |
await command('Page.navigate', dict(url=f'http://127.0.0.1:{server_port}/ui/login/huggingface?_target_url=/ui/'))
|
| 83 |
await wait("document.body.textContent.includes('audit-user')")
|
| 84 |
+
for width in (1440, 390):
|
| 85 |
+
await command('Emulation.setDeviceMetricsOverride', dict(width=width, height=1000, deviceScaleFactor=1, mobile=False))
|
| 86 |
+
for opened in (False, True):
|
| 87 |
+
state = await evaluate("""(() => {
|
| 88 |
+
const shell=document.querySelector('.submission-center-shell');
|
| 89 |
+
const header=shell.querySelector('.submission-panel > button.label-wrap');
|
| 90 |
+
if (header.classList.contains('open')!==%s) header.click();
|
| 91 |
+
return {visibleRefresh:Array.from(shell.querySelectorAll(':scope > button.board-refresh')).some(el=>el.getBoundingClientRect().height>0),
|
| 92 |
+
titleFits:header.scrollWidth<=header.clientWidth+1};
|
| 93 |
+
})()""" % str(opened).lower())
|
| 94 |
+
assert not state['visibleRefresh'] and state['titleFits'], (width, opened, state)
|
| 95 |
+
print('PASS evaluator refresh hidden, desktop/mobile, expanded/collapsed, no header gap', flush=True)
|
| 96 |
await evaluate("document.querySelector('.submission-panel > button').classList.contains('open') || document.querySelector('.submission-panel > button').click()")
|
| 97 |
await wait("Array.from(document.querySelectorAll('.submission-panel .utility-tabs [role=tab]')).some(el=>el.textContent.trim()==='History & Reports')")
|
| 98 |
await click("Array.from(document.querySelectorAll('.submission-panel .utility-tabs [role=tab]')).find(el=>el.textContent.trim()==='History & Reports')")
|
frontend_interaction_regression.py
CHANGED
|
@@ -142,7 +142,10 @@ class InteractionChecks(unittest.TestCase):
|
|
| 142 |
self.assertIn('Novel View Synthesis', html)
|
| 143 |
self.assertIn('0.000', html)
|
| 144 |
record['metrics']['rendering_pmf'] = 0
|
| 145 |
-
|
|
|
|
|
|
|
|
|
|
| 146 |
self.assertEqual(record['metrics']['novel_pmf'], original['metrics']['novel_pmf'])
|
| 147 |
|
| 148 |
def test_input_source_metadata_is_allowlisted_and_not_paper_or_code(self):
|
|
@@ -163,6 +166,9 @@ class InteractionChecks(unittest.TestCase):
|
|
| 163 |
status = [item for item in components if (item['props'].get('elem_id') or '').startswith('evaluator-status-')]
|
| 164 |
self.assertEqual(len(status), 4)
|
| 165 |
self.assertTrue(all(item['props']['visible'] is False for item in status))
|
|
|
|
|
|
|
|
|
|
| 166 |
text = (Path(__file__).parent/'app.py').read_text(encoding='utf-8')
|
| 167 |
self.assertNotIn('Enter Method and User Dataset before submitting.', text)
|
| 168 |
self.assertNotIn('Sign in with Hugging Face to create or load your Researcher Identity.', text)
|
|
|
|
| 142 |
self.assertIn('Novel View Synthesis', html)
|
| 143 |
self.assertIn('0.000', html)
|
| 144 |
record['metrics']['rendering_pmf'] = 0
|
| 145 |
+
with_overall = self.ui.render_submission_summary(record, {})
|
| 146 |
+
self.assertIn('Rendering PMF', with_overall)
|
| 147 |
+
self.assertIn('<h5>Overall Rendering Metrics</h5>', with_overall)
|
| 148 |
+
self.assertNotIn('<h5>Metrics</h5>', with_overall)
|
| 149 |
self.assertEqual(record['metrics']['novel_pmf'], original['metrics']['novel_pmf'])
|
| 150 |
|
| 151 |
def test_input_source_metadata_is_allowlisted_and_not_paper_or_code(self):
|
|
|
|
| 166 |
status = [item for item in components if (item['props'].get('elem_id') or '').startswith('evaluator-status-')]
|
| 167 |
self.assertEqual(len(status), 4)
|
| 168 |
self.assertTrue(all(item['props']['visible'] is False for item in status))
|
| 169 |
+
refresh = [item for item in components if (item['props'].get('elem_id') or '').startswith('evaluator-refresh-')]
|
| 170 |
+
self.assertEqual(len(refresh), 4)
|
| 171 |
+
self.assertTrue(all(item['props']['visible'] is False for item in refresh))
|
| 172 |
text = (Path(__file__).parent/'app.py').read_text(encoding='utf-8')
|
| 173 |
self.assertNotIn('Enter Method and User Dataset before submitting.', text)
|
| 174 |
self.assertNotIn('Sign in with Hugging Face to create or load your Researcher Identity.', text)
|
queue_position_regression.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Synthetic global waiting-position tests; no Worker, Hub or production writes."""
|
| 2 |
+
import ast
|
| 3 |
+
from contextlib import ExitStack
|
| 4 |
+
from datetime import datetime, timezone
|
| 5 |
+
import json
|
| 6 |
+
import os
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import tempfile
|
| 9 |
+
import unittest
|
| 10 |
+
from unittest.mock import patch
|
| 11 |
+
|
| 12 |
+
import staging_regression as staging
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class QueuePositionChecks(unittest.TestCase):
|
| 16 |
+
@classmethod
|
| 17 |
+
def setUpClass(cls):
|
| 18 |
+
staging.StagingChecks.setUpClass()
|
| 19 |
+
cls.ui = staging.StagingChecks.ui
|
| 20 |
+
|
| 21 |
+
@classmethod
|
| 22 |
+
def tearDownClass(cls):
|
| 23 |
+
staging.StagingChecks.tearDownClass()
|
| 24 |
+
|
| 25 |
+
def setUp(self):
|
| 26 |
+
from src.storage import local
|
| 27 |
+
from src.leaderboard import read_evals, queue_position
|
| 28 |
+
staging.StagingChecks().seed_identity()
|
| 29 |
+
self.local, self.reader, self.positions = local, read_evals, queue_position
|
| 30 |
+
self.owner = staging.StagingChecks.owner
|
| 31 |
+
self.identity = '0000-0002-1825-0097'
|
| 32 |
+
self.temporary = tempfile.TemporaryDirectory(prefix='phys-position-')
|
| 33 |
+
self.addCleanup(self.temporary.cleanup)
|
| 34 |
+
self.root = Path(self.temporary.name)
|
| 35 |
+
self.stack = self.enterContext(ExitStack())
|
| 36 |
+
for name, relative in dict(PENDING_DIR='pending', PROCESSING_DIR='processing', COMPLETE_DIR='complete',
|
| 37 |
+
PROGRESS_DIR='progress', DELETE_PENDING_DIR='deletions/pending',
|
| 38 |
+
DELETE_PROCESSING_DIR='deletions/processing').items():
|
| 39 |
+
directory = self.root/relative
|
| 40 |
+
directory.mkdir(parents=True, exist_ok=True)
|
| 41 |
+
self.stack.enter_context(patch.object(local, name, str(directory)))
|
| 42 |
+
(self.root/'deletions/complete').mkdir()
|
| 43 |
+
self.stack.enter_context(patch.object(read_evals, 'get_submitter', return_value=dict(researcher_identity_id=self.identity)))
|
| 44 |
+
self.stack.enter_context(patch.object(read_evals, 'hidden_submission_ids', return_value=set()))
|
| 45 |
+
self.marker = {}
|
| 46 |
+
self.stack.enter_context(patch.object(queue_position, '_read', side_effect=lambda: dict(orcid_maintenance=self.marker)))
|
| 47 |
+
|
| 48 |
+
def record(self, number, *, owner=None, task='motionTransfer', status='pending', managed=True, time=1):
|
| 49 |
+
sid = f'{number:010x}'
|
| 50 |
+
record = dict(submission_id=sid, task_name=task, status=status,
|
| 51 |
+
researcher_identity_id=owner or self.identity, hf_subject='subject-'+str(number),
|
| 52 |
+
display_name='Private method '+str(number), team_id='Private team '+str(number),
|
| 53 |
+
submitted_at=f'2026-11-01T00:00:{time:02d}Z', scenes_total=4)
|
| 54 |
+
if managed:
|
| 55 |
+
record['evaluator_dispatch_ready'] = True
|
| 56 |
+
directory = 'processing' if status == 'running' else 'pending'
|
| 57 |
+
path = self.root/directory/(sid+'.json')
|
| 58 |
+
path.write_text(json.dumps(record), encoding='utf-8')
|
| 59 |
+
os.utime(path, (100+number, 100+number))
|
| 60 |
+
return record, path
|
| 61 |
+
|
| 62 |
+
def queue(self, tasks=None, limit=100):
|
| 63 |
+
return self.reader.load_queue_df(limit=limit, task_names=tasks or ['motionTransfer'], owner_username=self.owner).set_index('ID')
|
| 64 |
+
|
| 65 |
+
def test_empty_and_single(self):
|
| 66 |
+
self.assertTrue(self.queue().empty)
|
| 67 |
+
record, _ = self.record(1)
|
| 68 |
+
self.assertEqual(self.queue().loc[record['submission_id'], 'Status'], 'Queued · #1')
|
| 69 |
+
|
| 70 |
+
def test_global_users_tasks_ranked_before_private_filters_and_limit(self):
|
| 71 |
+
self.record(1, owner='other-identity', task='futurePrediction', time=1)
|
| 72 |
+
mine, _ = self.record(2, time=2)
|
| 73 |
+
other_task, _ = self.record(3, task='propertyEstimation', time=3)
|
| 74 |
+
last, _ = self.record(4, time=4)
|
| 75 |
+
rows = self.queue()
|
| 76 |
+
self.assertEqual(set(rows.index), {mine['submission_id'], last['submission_id']})
|
| 77 |
+
self.assertEqual(rows.loc[mine['submission_id'], 'Status'], 'Queued · #2')
|
| 78 |
+
self.assertEqual(rows.loc[last['submission_id'], 'Status'], 'Queued · #4')
|
| 79 |
+
self.assertNotIn('Private method 1', rows.to_json())
|
| 80 |
+
self.assertNotIn(other_task['submission_id'], rows.index)
|
| 81 |
+
self.assertEqual(self.queue(limit=1).iloc[0]['Status'], 'Queued · #4')
|
| 82 |
+
|
| 83 |
+
def test_worker_managed_time_legacy_mtime_and_filename_tie_break(self):
|
| 84 |
+
first, first_path = self.record(9, managed=False, time=59)
|
| 85 |
+
second, second_path = self.record(8, time=2)
|
| 86 |
+
third, third_path = self.record(7, time=2)
|
| 87 |
+
os.utime(second_path, (1, 1))
|
| 88 |
+
os.utime(third_path, (2, 2))
|
| 89 |
+
rows = self.queue()
|
| 90 |
+
for record, position in [(first, 1), (third, 2), (second, 3)]:
|
| 91 |
+
self.assertEqual(rows.loc[record['submission_id'], 'Status'], f'Queued · #{position}')
|
| 92 |
+
source = ast.parse((Path(__file__).parent.parent/'PhysInOne-Leaderboard-Worker/src/worker/lease.py').read_text(encoding='utf-8'))
|
| 93 |
+
claim = next(node for node in source.body if isinstance(node, ast.FunctionDef) and node.name == 'claim_one')
|
| 94 |
+
body = []
|
| 95 |
+
for node in claim.body:
|
| 96 |
+
if isinstance(node, ast.For) and isinstance(node.target, ast.Tuple):
|
| 97 |
+
break
|
| 98 |
+
body.append(node)
|
| 99 |
+
body.append(ast.Return(value=ast.Name(id='entries', ctx=ast.Load())))
|
| 100 |
+
function = ast.FunctionDef(name='worker_order', args=ast.arguments(posonlyargs=[], args=[], kwonlyargs=[], kw_defaults=[], defaults=[]), body=body, decorator_list=[])
|
| 101 |
+
module = ast.fix_missing_locations(ast.Module(body=[function], type_ignores=[]))
|
| 102 |
+
scope = dict(PENDING_DIR=str(self.root/'pending'), os=os,
|
| 103 |
+
list_queue=lambda root: sorted(path.name for path in Path(root).glob('*.json')),
|
| 104 |
+
read_json=lambda path: json.loads(Path(path).read_text()),
|
| 105 |
+
parse_iso=lambda value: datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=timezone.utc))
|
| 106 |
+
exec(compile(module, '<readonly-worker-order>', 'exec'), scope)
|
| 107 |
+
expected = {entry[2]['submission_id']: index for index, entry in enumerate(scope['worker_order'](), 1)}
|
| 108 |
+
self.assertEqual(self.positions.waiting_positions(self.local.list_pending(), [], set()), expected)
|
| 109 |
+
|
| 110 |
+
def test_running_progress_excluded_and_position_moves_on_claim(self):
|
| 111 |
+
first, path = self.record(1)
|
| 112 |
+
second, _ = self.record(2, time=2)
|
| 113 |
+
first['status'] = 'running'
|
| 114 |
+
target = self.root/'processing'/path.name
|
| 115 |
+
target.write_text(json.dumps(first))
|
| 116 |
+
path.unlink()
|
| 117 |
+
(self.root/'progress'/path.name).write_text(json.dumps(dict(scene_results={'scene': {}})))
|
| 118 |
+
rows = self.queue()
|
| 119 |
+
self.assertEqual(rows.loc[first['submission_id'], 'Status'], 'Running')
|
| 120 |
+
self.assertEqual(rows.loc[first['submission_id'], 'Progress / Scenes'], '1/4')
|
| 121 |
+
self.assertEqual(rows.loc[second['submission_id'], 'Status'], 'Queued · #1')
|
| 122 |
+
|
| 123 |
+
def test_finished_cancelled_failed_leave_waiting_order(self):
|
| 124 |
+
for status in ('done', 'cancelled', 'failed'):
|
| 125 |
+
with self.subTest(status=status):
|
| 126 |
+
first, path = self.record(1)
|
| 127 |
+
second, second_path = self.record(2, time=2)
|
| 128 |
+
first['status'] = status
|
| 129 |
+
(self.root/'complete'/path.name).write_text(json.dumps(first))
|
| 130 |
+
path.unlink()
|
| 131 |
+
rows = self.queue()
|
| 132 |
+
self.assertEqual(list(rows.index), [second['submission_id']])
|
| 133 |
+
self.assertEqual(rows.iloc[0]['Status'], 'Queued · #1')
|
| 134 |
+
second_path.unlink()
|
| 135 |
+
|
| 136 |
+
def test_unready_head_is_not_skipped(self):
|
| 137 |
+
first, path = self.record(1)
|
| 138 |
+
first['evaluator_dispatch_ready'] = False
|
| 139 |
+
path.write_text(json.dumps(first))
|
| 140 |
+
second, _ = self.record(2, time=2)
|
| 141 |
+
rows = self.queue()
|
| 142 |
+
self.assertEqual(rows.loc[first['submission_id'], 'Status'], 'Queued · #1')
|
| 143 |
+
self.assertEqual(rows.loc[second['submission_id'], 'Status'], 'Queued · #2')
|
| 144 |
+
|
| 145 |
+
def test_maintenance_skips_affected_identity_and_hides_its_position(self):
|
| 146 |
+
first, _ = self.record(1)
|
| 147 |
+
second, _ = self.record(2, owner='other-identity', time=2)
|
| 148 |
+
self.marker = dict(phase='awaiting_oauth', old_orcid=self.identity, new_orcid='target', hf_subject='admin')
|
| 149 |
+
self.assertEqual(self.queue().loc[first['submission_id'], 'Status'], 'Queued')
|
| 150 |
+
self.assertEqual(self.positions.waiting_positions(self.local.list_pending(), [], set()), {second['submission_id']: 1})
|
| 151 |
+
self.marker['phase'] = 'applying'
|
| 152 |
+
self.assertEqual(self.positions.waiting_positions(self.local.list_pending(), [], set()), {})
|
| 153 |
+
|
| 154 |
+
def test_uncertain_order_omits_numbers(self):
|
| 155 |
+
record, _ = self.record(1)
|
| 156 |
+
with patch.object(self.positions.os.path, 'getmtime', side_effect=OSError('synthetic')):
|
| 157 |
+
record.pop('evaluator_dispatch_ready')
|
| 158 |
+
(self.root/'pending'/(record['submission_id']+'.json')).write_text(json.dumps(record))
|
| 159 |
+
self.assertEqual(self.queue().iloc[0]['Status'], 'Queued')
|
| 160 |
+
with patch.object(self.positions, '_read', side_effect=OSError('synthetic')):
|
| 161 |
+
self.assertEqual(self.queue().iloc[0]['Status'], 'Queued')
|
| 162 |
+
|
| 163 |
+
def test_pending_cancel_request_and_terminal_residual_are_not_guessed(self):
|
| 164 |
+
first, path = self.record(1)
|
| 165 |
+
self.record(2, time=2)
|
| 166 |
+
request = dict(first, request_id='abcdef', action='cancel_submission')
|
| 167 |
+
(self.root/'deletions/pending/abcdef.json').write_text(json.dumps(request))
|
| 168 |
+
self.assertTrue(all(value == 'Queued' for value in self.queue()['Status']))
|
| 169 |
+
(self.root/'deletions/pending/abcdef.json').unlink()
|
| 170 |
+
(self.root/'complete'/path.name).write_text(json.dumps(dict(first, status='done')))
|
| 171 |
+
self.assertTrue(all(value == 'Queued' for value in self.queue()['Status']))
|
| 172 |
+
|
| 173 |
+
def test_repeated_refresh_read_only_and_no_sleep_wake_calls(self):
|
| 174 |
+
self.record(1)
|
| 175 |
+
before = {str(path):(path.read_bytes(), path.stat().st_mtime_ns) for path in self.root.rglob('*') if path.is_file()}
|
| 176 |
+
from src.submission import evaluator_space
|
| 177 |
+
with patch.object(evaluator_space, 'HfApi', side_effect=AssertionError('must not contact Worker')), \
|
| 178 |
+
patch.object(evaluator_space, 'submission_enqueued', side_effect=AssertionError('must not write')):
|
| 179 |
+
for _ in range(3):
|
| 180 |
+
self.assertEqual(self.queue().iloc[0]['Status'], 'Queued · #1')
|
| 181 |
+
self.assertEqual(before, {str(path):(path.read_bytes(), path.stat().st_mtime_ns) for path in self.root.rglob('*') if path.is_file()})
|
| 182 |
+
|
| 183 |
+
def test_only_queue_status_width_changes(self):
|
| 184 |
+
from src.display.table_layout import column_width
|
| 185 |
+
self.assertEqual(column_width('Status'), 132)
|
| 186 |
+
components = self.ui.demo.config['components']
|
| 187 |
+
queue = next(item for item in components if item['type'] == 'dataframe' and 'queue-table' in item['props']['elem_classes'])
|
| 188 |
+
history = next(item for item in components if item['type'] == 'dataframe' and 'history-table' in item['props']['elem_classes'])
|
| 189 |
+
for item, width in ((queue, 164), (history, 132)):
|
| 190 |
+
self.assertEqual(item['props']['column_widths'][item['props']['headers'].index('Status')], f'{width}px')
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
if __name__ == '__main__':
|
| 194 |
+
unittest.main(verbosity=2)
|
queue_refresh_regression.py
CHANGED
|
@@ -113,7 +113,7 @@ async def browser_check(port, server_port, api, runtime, root):
|
|
| 113 |
if await evaluate(f'Boolean(document.body && ({expression}))'):
|
| 114 |
return
|
| 115 |
await asyncio.sleep(.1)
|
| 116 |
-
raise AssertionError(expression)
|
| 117 |
|
| 118 |
await command('Network.enable', {})
|
| 119 |
await command('Network.setBlockedURLs', dict(urls=['https://*']))
|
|
@@ -124,13 +124,49 @@ async def browser_check(port, server_port, api, runtime, root):
|
|
| 124 |
await evaluate("document.querySelector('#video_generation-utility-queue-button').click()")
|
| 125 |
selector = '#video_generation-queue-refresh-button'
|
| 126 |
await wait(f"document.querySelector('{selector}')?.getBoundingClientRect().height>0")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
for width in (1440, 390):
|
| 128 |
await command('Emulation.setDeviceMetricsOverride', dict(width=width, height=900, deviceScaleFactor=1, mobile=False))
|
| 129 |
await evaluate(f"document.querySelector('{selector}').scrollIntoView({{block:'center'}})")
|
| 130 |
await asyncio.sleep(.2)
|
| 131 |
assert await evaluate(f"""(() => {{const button=document.querySelector('{selector}'),box=button.getBoundingClientRect(),title=button.parentElement.querySelector('.submission-section-title').getBoundingClientRect();
|
| 132 |
return box.x>=title.right-1 && [.1,.5,.9].every(fraction=>{{const hit=document.elementFromPoint(box.x+box.width*fraction,box.y+box.height/2);return button===hit||button.contains(hit)}});}})()"""), width
|
| 133 |
-
frame = ui._safe_queue([next(iter(ui.TASKS))], staging.StagingChecks.owner)
|
| 134 |
record = {column: '' for column in frame.columns}
|
| 135 |
record.update(ID='123456abcd', Task=next(iter(ui.TASKS)), Status='running')
|
| 136 |
live = pd.DataFrame([record], columns=frame.columns)
|
|
|
|
| 113 |
if await evaluate(f'Boolean(document.body && ({expression}))'):
|
| 114 |
return
|
| 115 |
await asyncio.sleep(.1)
|
| 116 |
+
raise AssertionError((expression, await evaluate("({queue:document.querySelector('#submit-video_generation')?.innerText.slice(-6000),tables:document.querySelectorAll('.queue-table').length})")))
|
| 117 |
|
| 118 |
await command('Network.enable', {})
|
| 119 |
await command('Network.setBlockedURLs', dict(urls=['https://*']))
|
|
|
|
| 124 |
await evaluate("document.querySelector('#video_generation-utility-queue-button').click()")
|
| 125 |
selector = '#video_generation-queue-refresh-button'
|
| 126 |
await wait(f"document.querySelector('{selector}')?.getBoundingClientRect().height>0")
|
| 127 |
+
from src import envs
|
| 128 |
+
from src.submission import evaluator_space
|
| 129 |
+
runtime.stage = 'SLEEPING'
|
| 130 |
+
fixture_task = next(iter(ui.TASKS))
|
| 131 |
+
foreign = dict(submission_id='a111111111', task_name='futurePrediction', status='queued',
|
| 132 |
+
researcher_identity_id='private-other-user', display_name='PRIVATE_OTHER_METHOD',
|
| 133 |
+
submitted_at='2026-11-01T00:00:01Z', evaluator_dispatch_ready=True)
|
| 134 |
+
mine = dict(foreign, submission_id='a222222222', task_name=fixture_task,
|
| 135 |
+
researcher_identity_id='0000-0002-1825-0097', display_name='Queue fixture',
|
| 136 |
+
submitted_at='2026-11-01T00:00:02Z')
|
| 137 |
+
running = dict(mine, submission_id='a333333333', status='running', scenes_total=4)
|
| 138 |
+
for directory, record in ((envs.PENDING_DIR, foreign), (envs.PENDING_DIR, mine), (envs.PROCESSING_DIR, running)):
|
| 139 |
+
(Path(directory)/(record['submission_id']+'.json')).write_text(json.dumps(record))
|
| 140 |
+
(Path(envs.PROGRESS_DIR)/(running['submission_id']+'.json')).write_text(json.dumps(dict(scene_results={'scene': {}})))
|
| 141 |
+
with patch.object(evaluator_space, 'HfApi', side_effect=AssertionError('Queue refresh must not contact Worker')):
|
| 142 |
+
await asyncio.sleep(.6)
|
| 143 |
+
await evaluate("document.querySelector('#video_generation-utility-queue-button').click()")
|
| 144 |
+
await wait("document.querySelector('#submit-video_generation .queue-table')")
|
| 145 |
+
await evaluate(f"document.querySelector('{selector}').click()")
|
| 146 |
+
await wait("document.querySelector('#submit-video_generation .queue-table')?.textContent.includes('Queued · #2')")
|
| 147 |
+
assert not await evaluate("document.querySelector('#submit-video_generation .queue-table').textContent.includes('PRIVATE_OTHER_METHOD')")
|
| 148 |
+
for width in (1440, 390):
|
| 149 |
+
await command('Emulation.setDeviceMetricsOverride', dict(width=width, height=900, deviceScaleFactor=1, mobile=False))
|
| 150 |
+
await asyncio.sleep(.2)
|
| 151 |
+
assert await evaluate("""(() => {
|
| 152 |
+
const table=document.querySelector('#submit-video_generation .queue-table table:not([aria-hidden=true])');
|
| 153 |
+
const column=Array.from(table.querySelectorAll('thead th')).findIndex(el=>el.textContent.includes('Status'));
|
| 154 |
+
return Array.from(table.tBodies[0].rows).every(row=>row.cells[column].scrollWidth<=row.cells[column].clientWidth+1)
|
| 155 |
+
&&table.textContent.includes('Queued · #2')&&table.textContent.includes('Running')&&table.textContent.includes('1/4');
|
| 156 |
+
})()"""), width
|
| 157 |
+
foreign['status'] = 'running'
|
| 158 |
+
(Path(envs.PROCESSING_DIR)/(foreign['submission_id']+'.json')).write_text(json.dumps(foreign))
|
| 159 |
+
(Path(envs.PENDING_DIR)/(foreign['submission_id']+'.json')).unlink()
|
| 160 |
+
await evaluate(f"document.querySelector('{selector}').click()")
|
| 161 |
+
await wait("document.querySelector('#submit-video_generation .queue-table')?.textContent.includes('Queued · #1')")
|
| 162 |
+
print('PASS global queue positions #2 -> #1, private rows hidden, Running progress retained, desktop/mobile Status fits, Sleeping Worker never contacted', flush=True)
|
| 163 |
for width in (1440, 390):
|
| 164 |
await command('Emulation.setDeviceMetricsOverride', dict(width=width, height=900, deviceScaleFactor=1, mobile=False))
|
| 165 |
await evaluate(f"document.querySelector('{selector}').scrollIntoView({{block:'center'}})")
|
| 166 |
await asyncio.sleep(.2)
|
| 167 |
assert await evaluate(f"""(() => {{const button=document.querySelector('{selector}'),box=button.getBoundingClientRect(),title=button.parentElement.querySelector('.submission-section-title').getBoundingClientRect();
|
| 168 |
return box.x>=title.right-1 && [.1,.5,.9].every(fraction=>{{const hit=document.elementFromPoint(box.x+box.width*fraction,box.y+box.height/2);return button===hit||button.contains(hit)}});}})()"""), width
|
| 169 |
+
frame = ui._safe_queue([next(iter(ui.TASKS))], staging.StagingChecks.owner).iloc[:0].copy()
|
| 170 |
record = {column: '' for column in frame.columns}
|
| 171 |
record.update(ID='123456abcd', Task=next(iter(ui.TASKS)), Status='running')
|
| 172 |
live = pd.DataFrame([record], columns=frame.columns)
|
src/display/academic.py
CHANGED
|
@@ -294,6 +294,8 @@ button.open-submission:hover { box-shadow: 0 5px 18px rgba(99,102,241,.28) !impo
|
|
| 294 |
.gradio-container #main-tabs .task-page-shell button.board-refresh { display: inline-flex !important; align-items: center !important; justify-content: center !important; gap: 6px !important; flex: 0 0 auto !important; width: auto !important; min-width: 0 !important; min-height: 32px !important; padding: 4px 10px !important; border: 1px solid #e5e2f0 !important; border-radius: 7px !important; background: #fff !important; color: #5149b8 !important; box-shadow: none !important; font-size: 12px !important; font-weight: 500 !important; line-height: 20px !important; }
|
| 295 |
.gradio-container #main-tabs .task-page-shell button.board-refresh:hover { background: #f1efff !important; color: #6366f1 !important; }
|
| 296 |
.gradio-container #main-tabs .task-page-shell button.board-refresh:disabled { opacity: .5 !important; cursor: not-allowed; }
|
|
|
|
|
|
|
| 297 |
.public-leaderboard { margin: 0 !important; padding: 0 !important; border: 0 !important; overflow: visible !important; }
|
| 298 |
.public-leaderboard > .html-container { padding: 0 !important; min-width: 0; overflow: visible !important; }
|
| 299 |
.public-leaderboard .board-scroll { overflow: auto !important; width: 100%; border: 1px solid #e0ddf0; border-radius: 15px; background: #ffffff; box-shadow: 0 5px 18px rgba(30,41,59,.035),0 0 22px rgba(139,92,246,.035); scrollbar-width: thin; scrollbar-color: #d6d7e5 transparent; }
|
|
|
|
| 294 |
.gradio-container #main-tabs .task-page-shell button.board-refresh { display: inline-flex !important; align-items: center !important; justify-content: center !important; gap: 6px !important; flex: 0 0 auto !important; width: auto !important; min-width: 0 !important; min-height: 32px !important; padding: 4px 10px !important; border: 1px solid #e5e2f0 !important; border-radius: 7px !important; background: #fff !important; color: #5149b8 !important; box-shadow: none !important; font-size: 12px !important; font-weight: 500 !important; line-height: 20px !important; }
|
| 295 |
.gradio-container #main-tabs .task-page-shell button.board-refresh:hover { background: #f1efff !important; color: #6366f1 !important; }
|
| 296 |
.gradio-container #main-tabs .task-page-shell button.board-refresh:disabled { opacity: .5 !important; cursor: not-allowed; }
|
| 297 |
+
.gradio-container #main-tabs .submission-center-shell { position: relative; gap: 0 !important; padding: 0 !important; }
|
| 298 |
+
.gradio-container #main-tabs .submission-center-shell > .submission-panel { flex: 1 1 100%; width: 100%; min-width: 0 !important; }
|
| 299 |
.public-leaderboard { margin: 0 !important; padding: 0 !important; border: 0 !important; overflow: visible !important; }
|
| 300 |
.public-leaderboard > .html-container { padding: 0 !important; min-width: 0; overflow: visible !important; }
|
| 301 |
.public-leaderboard .board-scroll { overflow: auto !important; width: 100%; border: 1px solid #e0ddf0; border-radius: 15px; background: #ffffff; box-shadow: 0 5px 18px rgba(30,41,59,.035),0 0 22px rgba(139,92,246,.035); scrollbar-width: thin; scrollbar-color: #d6d7e5 transparent; }
|
src/display/table_layout.py
CHANGED
|
@@ -125,7 +125,7 @@ table_resize_js = r"""() => {
|
|
| 125 |
}
|
| 126 |
});
|
| 127 |
if (!saved.has(index)) {
|
| 128 |
-
saved.set(index, Math.max(minimumFor(name), initialWidthFor(name)));
|
| 129 |
}
|
| 130 |
if (header.querySelector('.column-resize-handle')) return;
|
| 131 |
const setWidth = requested => {
|
|
|
|
| 125 |
}
|
| 126 |
});
|
| 127 |
if (!saved.has(index)) {
|
| 128 |
+
saved.set(index, Math.max(minimumFor(name), name === 'Status' && component.classList.contains('queue-table') ? 164 : initialWidthFor(name)));
|
| 129 |
}
|
| 130 |
if (header.querySelector('.column-resize-handle')) return;
|
| 131 |
const setWidth = requested => {
|
src/leaderboard/queue_position.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Read-only pending positions matching Worker claim_one ordering."""
|
| 2 |
+
from datetime import datetime, timezone
|
| 3 |
+
import logging
|
| 4 |
+
import math
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import re
|
| 8 |
+
|
| 9 |
+
from src.storage import local
|
| 10 |
+
from src.submission.management_queue import active_requests
|
| 11 |
+
from src.submission.registry import _read
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def waiting_positions(pending: list[dict], processing: list[dict], terminal_ids: set[str]) -> dict[str, int]:
|
| 17 |
+
if not pending:
|
| 18 |
+
return {}
|
| 19 |
+
try:
|
| 20 |
+
names = local._list_json(local.PENDING_DIR, strict=True)
|
| 21 |
+
by_id = {record.get('submission_id'): record for record in pending}
|
| 22 |
+
if (len(by_id) != len(pending) or any(not isinstance(sid, str) or not re.fullmatch(r'[0-9a-f]{10}', sid) for sid in by_id)
|
| 23 |
+
or set(names) != {sid+'.json' for sid in by_id}):
|
| 24 |
+
return {}
|
| 25 |
+
processing_ids = {record.get('submission_id') for record in processing}
|
| 26 |
+
maintenance = _read().get('orcid_maintenance') or {}
|
| 27 |
+
if not isinstance(maintenance, dict) or maintenance.get('phase') == 'applying':
|
| 28 |
+
return {}
|
| 29 |
+
|
| 30 |
+
def blocked(record):
|
| 31 |
+
return bool(maintenance and (record.get('researcher_identity_id') in
|
| 32 |
+
(maintenance.get('old_orcid'), maintenance.get('new_orcid'))
|
| 33 |
+
or record.get('hf_subject') == maintenance.get('hf_subject')))
|
| 34 |
+
|
| 35 |
+
requests = active_requests(local.DELETE_PENDING_DIR, local.DELETE_PROCESSING_DIR,
|
| 36 |
+
Path(local.DELETE_PENDING_DIR).parent/'complete')
|
| 37 |
+
for _, request in requests:
|
| 38 |
+
record = by_id.get(request.get('submission_id'))
|
| 39 |
+
if (record and not blocked(request) and request.get('action') in {'cancel_pending', 'cancel_submission'}
|
| 40 |
+
and request.get('researcher_identity_id')
|
| 41 |
+
and all(record.get(key) == request.get(key) for key in ('task_name', 'researcher_identity_id'))):
|
| 42 |
+
return {}
|
| 43 |
+
entries = []
|
| 44 |
+
for sid, record in by_id.items():
|
| 45 |
+
if sid in terminal_ids or sid in processing_ids:
|
| 46 |
+
return {}
|
| 47 |
+
if record.get('status') not in {'pending', 'queued'} or record.get('cancel_requested_at'):
|
| 48 |
+
return {}
|
| 49 |
+
if blocked(record):
|
| 50 |
+
continue
|
| 51 |
+
submitted = None
|
| 52 |
+
if 'evaluator_dispatch_ready' in record:
|
| 53 |
+
try:
|
| 54 |
+
submitted = datetime.strptime(record.get('submitted_at'), '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=timezone.utc)
|
| 55 |
+
except (TypeError, ValueError):
|
| 56 |
+
pass
|
| 57 |
+
order = submitted.timestamp() if submitted else os.path.getmtime(os.path.join(local.PENDING_DIR, sid+'.json'))
|
| 58 |
+
if not math.isfinite(order):
|
| 59 |
+
return {}
|
| 60 |
+
entries.append((order, sid))
|
| 61 |
+
if names != local._list_json(local.PENDING_DIR, strict=True):
|
| 62 |
+
return {}
|
| 63 |
+
return {sid: index for index, (_, sid) in enumerate(sorted(entries), 1)}
|
| 64 |
+
except (OSError, ValueError, TypeError):
|
| 65 |
+
logger.warning('Waiting queue order unavailable; omitting precise positions')
|
| 66 |
+
return {}
|
src/leaderboard/read_evals.py
CHANGED
|
@@ -25,6 +25,7 @@ from src.submission.public_metrics import public_metric_groups
|
|
| 25 |
from src.submission.status import display_submission_status, is_active_submission, is_terminal_submission
|
| 26 |
from src.submission.registry import get_submitter
|
| 27 |
from src.submission.publication import published_entries
|
|
|
|
| 28 |
|
| 29 |
logger = logging.getLogger(__name__)
|
| 30 |
|
|
@@ -322,6 +323,7 @@ def load_queue_df(
|
|
| 322 |
for record in all_processing:
|
| 323 |
if record.get('submission_id') and is_terminal_submission(record.get('status')):
|
| 324 |
completed_by_id.setdefault(record['submission_id'], record)
|
|
|
|
| 325 |
all_processing = [record for record in all_processing if record.get('submission_id') not in completed_by_id and is_active_submission(record.get('status') or 'running')]
|
| 326 |
active_ids = {record.get('submission_id') for record in all_processing}
|
| 327 |
all_pending = [record for record in all_pending if record.get('submission_id') not in completed_by_id and record.get('submission_id') not in active_ids and is_active_submission(record.get('status') or 'pending')]
|
|
@@ -334,19 +336,24 @@ def load_queue_df(
|
|
| 334 |
rows: list[dict] = []
|
| 335 |
|
| 336 |
for record in pending:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
rows.append({
|
| 338 |
"Submitted At": record.get("submitted_at"),
|
| 339 |
"ID": record.get("submission_id"),
|
| 340 |
"Submitter": normalize_submission_identity(record)["team_name"],
|
| 341 |
"Method / Model": normalize_submission_identity(record)["method_name"],
|
| 342 |
"Task": record.get("task_name"),
|
| 343 |
-
"Status":
|
| 344 |
"Progress / Scenes": "-",
|
| 345 |
"Score": None,
|
| 346 |
})
|
| 347 |
|
| 348 |
for record in processing:
|
| 349 |
submission_id = record.get("submission_id")
|
|
|
|
| 350 |
progress = read_progress(submission_id, strict=True) or {}
|
| 351 |
done = len(progress.get("scene_results") or {})
|
| 352 |
scene_failures = progress.get("scene_failures") or {}
|
|
@@ -357,7 +364,7 @@ def load_queue_df(
|
|
| 357 |
"Submitter": normalize_submission_identity(record)["team_name"],
|
| 358 |
"Method / Model": normalize_submission_identity(record)["method_name"],
|
| 359 |
"Task": record.get("task_name"),
|
| 360 |
-
"Status":
|
| 361 |
"Progress / Scenes": _scenes_str(done, failed, record.get("scenes_total")),
|
| 362 |
"Score": None,
|
| 363 |
})
|
|
|
|
| 25 |
from src.submission.status import display_submission_status, is_active_submission, is_terminal_submission
|
| 26 |
from src.submission.registry import get_submitter
|
| 27 |
from src.submission.publication import published_entries
|
| 28 |
+
from src.leaderboard.queue_position import waiting_positions
|
| 29 |
|
| 30 |
logger = logging.getLogger(__name__)
|
| 31 |
|
|
|
|
| 323 |
for record in all_processing:
|
| 324 |
if record.get('submission_id') and is_terminal_submission(record.get('status')):
|
| 325 |
completed_by_id.setdefault(record['submission_id'], record)
|
| 326 |
+
positions = waiting_positions(all_pending, all_processing, set(completed_by_id))
|
| 327 |
all_processing = [record for record in all_processing if record.get('submission_id') not in completed_by_id and is_active_submission(record.get('status') or 'running')]
|
| 328 |
active_ids = {record.get('submission_id') for record in all_processing}
|
| 329 |
all_pending = [record for record in all_pending if record.get('submission_id') not in completed_by_id and record.get('submission_id') not in active_ids and is_active_submission(record.get('status') or 'pending')]
|
|
|
|
| 336 |
rows: list[dict] = []
|
| 337 |
|
| 338 |
for record in pending:
|
| 339 |
+
status = display_submission_status({**record, "status": record.get("status") or "pending"})
|
| 340 |
+
if status in {"pending", "queued"}:
|
| 341 |
+
position = positions.get(record.get("submission_id"))
|
| 342 |
+
status = f"Queued · #{position}" if position else "Queued"
|
| 343 |
rows.append({
|
| 344 |
"Submitted At": record.get("submitted_at"),
|
| 345 |
"ID": record.get("submission_id"),
|
| 346 |
"Submitter": normalize_submission_identity(record)["team_name"],
|
| 347 |
"Method / Model": normalize_submission_identity(record)["method_name"],
|
| 348 |
"Task": record.get("task_name"),
|
| 349 |
+
"Status": status,
|
| 350 |
"Progress / Scenes": "-",
|
| 351 |
"Score": None,
|
| 352 |
})
|
| 353 |
|
| 354 |
for record in processing:
|
| 355 |
submission_id = record.get("submission_id")
|
| 356 |
+
status = display_submission_status({**record, "status": record.get("status") or "running"})
|
| 357 |
progress = read_progress(submission_id, strict=True) or {}
|
| 358 |
done = len(progress.get("scene_results") or {})
|
| 359 |
scene_failures = progress.get("scene_failures") or {}
|
|
|
|
| 364 |
"Submitter": normalize_submission_identity(record)["team_name"],
|
| 365 |
"Method / Model": normalize_submission_identity(record)["method_name"],
|
| 366 |
"Task": record.get("task_name"),
|
| 367 |
+
"Status": "Running" if status == "running" else status,
|
| 368 |
"Progress / Scenes": _scenes_str(done, failed, record.get("scenes_total")),
|
| 369 |
"Score": None,
|
| 370 |
})
|
src/submission/summary.py
CHANGED
|
@@ -100,7 +100,8 @@ def render_submission_summary(record: dict, links: dict, snapshot: dict | None =
|
|
| 100 |
if task.name == "propertyEstimation" and title in material_groups:
|
| 101 |
metric_groups.append(f'<details class="selected-metric-group"><summary>{escape(title)}</summary><div>{items}</div></details>')
|
| 102 |
else:
|
| 103 |
-
|
|
|
|
| 104 |
|
| 105 |
links_html = []
|
| 106 |
for kind in ("paper", "code"):
|
|
|
|
| 100 |
if task.name == "propertyEstimation" and title in material_groups:
|
| 101 |
metric_groups.append(f'<details class="selected-metric-group"><summary>{escape(title)}</summary><div>{items}</div></details>')
|
| 102 |
else:
|
| 103 |
+
display_title = "Overall Rendering Metrics" if task.name == "propertyEstimation" and title == "Metrics" else title
|
| 104 |
+
metric_groups.append(f'<section class="selected-metric-group"><h5>{escape(display_title)}</h5><div>{items}</div></section>')
|
| 105 |
|
| 106 |
links_html = []
|
| 107 |
for kind in ("paper", "code"):
|