| """Presentation-only HTML helpers. Original observations and scores are untouched.""" | |
| import difflib | |
| import html | |
| from pathlib import Path | |
| LABELS = {'english': 'English', 'hindi': 'Hindi', 'hinglish': 'Roman Hinglish', | |
| 'french': 'French', 'chinese': 'Chinese'} | |
| TASKS = {0: 'Middle drawer', 1: 'Bowl on stove', 2: 'Bottle on cabinet', | |
| 3: 'Top drawer and bowl', 4: 'Bowl on cabinet', 5: 'Push plate', | |
| 6: 'Cream cheese in bowl', 7: 'Turn on stove', 8: 'Bowl on plate', | |
| 9: 'Bottle on rack'} | |
| CSS = '''<style> | |
| .research {font-family:system-ui,sans-serif;color:#252b32;line-height:1.5} | |
| .research p {margin:.4rem 0 .7rem}.research h4{font-size:1rem;margin:.35rem 0} | |
| .research details {margin:.4rem 0}.research summary {cursor:pointer;font-size:.9rem;color:#385d79} | |
| .research pre {white-space:pre-wrap;overflow-wrap:anywhere;font-size:.85rem;max-height:360px;overflow:auto} | |
| .research img {max-width:100%;height:auto}.research figure{margin:0} | |
| .research figcaption{font-size:.85rem;color:#505b66;margin-top:.35rem} | |
| .research .comparison {display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:20px;margin:1rem 0} | |
| .research .media-comparison {grid-template-columns:repeat(auto-fit,minmax(260px,420px))} | |
| .research .image-answer {display:grid;grid-template-columns:minmax(230px,32%) minmax(0,1fr);gap:22px;margin:1rem 0 1.6rem} | |
| .research .image-answer>figure {align-self:start}.research .image-answer img {width:100%;max-height:330px;object-fit:contain} | |
| .research .views {display:grid;grid-template-columns:1fr 1fr;gap:16px;margin:.7rem 0} | |
| .research .views img {width:100%;image-rendering:auto;max-height:360px;object-fit:contain} | |
| .research .scroll {overflow-x:auto;width:100%;margin:.8rem 0} | |
| .research table {border-collapse:collapse;width:100%;text-align:left;font-size:.9rem} | |
| .research table th,.research table td {padding:9px 12px;border-bottom:1px solid #dbe0e5;vertical-align:top;text-align:left!important} | |
| .research .prediction-table {table-layout:fixed}.research .prediction-table th:nth-child(1){width:14%}.research .prediction-table th:nth-child(2){width:48%}.research .prediction-table th:nth-child(3){width:12%}.research .prediction-table th:nth-child(4){width:26%} | |
| .research th {background:#f5f7f9;font-weight:600}.research .scroll table{min-width:660px} | |
| .research .matrix-scroll {overflow-x:auto;padding-bottom:16px} | |
| .research .rollout-matrix {display:grid;grid-template-columns:repeat(5,minmax(230px,1fr));gap:14px;min-width:1220px} | |
| .research .matrix-heading {border-bottom:2px solid #bcc8d1;padding:8px 6px;font-size:.95rem;min-height:86px} | |
| .research .matrix-heading p{font-size:.85rem;margin:.3rem 0} | |
| .research .video-card{min-width:0;border-bottom:1px solid #dbe0e5;padding:6px 0 12px} | |
| .research video{width:100%;height:auto;aspect-ratio:1;object-fit:contain;background:#f1f3f5;display:block;margin:8px 0} | |
| .research .video-meta{display:flex;justify-content:space-between;gap:8px;font-size:.85rem} | |
| .research .outcome{font-weight:600}.research .failure{color:#795242}.research .success{color:#275c73} | |
| .research .video-card>a{font-size:.85rem}.research .instruction{font-size:.9rem;min-height:2.8em} | |
| .research mark{background:#e8edf4;color:#204b72;font-weight:600;padding:0 2px} | |
| .research .frames {display:grid;grid-template-columns:repeat(4,minmax(170px,1fr));gap:10px;min-width:740px} | |
| .research .frames img{width:100%}.research .note{font-size:.9rem;color:#505b66} | |
| @media(max-width:760px){.research .image-answer{grid-template-columns:1fr}.research .comparison{grid-template-columns:1fr}.research .views{grid-template-columns:1fr 1fr;gap:8px}} | |
| @media print{.research video{display:none}.research .rollout-matrix{min-width:0;grid-template-columns:repeat(5,1fr)}.research .matrix-scroll{overflow:visible}} | |
| </style>''' | |
| def esc(value): | |
| # Display punctuation only. Exact source strings remain in linked raw records. | |
| return html.escape(str(value).replace('\u2014', '; ')) | |
| def wrap(body): return '<div class="research">' + body + '</div>' | |
| def exact_block(text, label='Saved prompt / response'): | |
| return '<details><summary>' + esc(label) + '</summary><pre>' + esc(text) + '</pre></details>' | |
| def table(frame): return wrap('<div class="scroll">' + frame.to_html(index=False,escape=True).replace('\u2014','; ') + '</div>') | |
| def highlight_change(original, modified): | |
| left, right = original.split(), modified.split() | |
| chunks=[] | |
| for tag,a,b,c,d in difflib.SequenceMatcher(None,left,right,autojunk=False).get_opcodes(): | |
| if c==d: continue | |
| words=esc(' '.join(right[c:d])) | |
| chunks.append(words if tag=='equal' else '<mark>'+words+'</mark>') | |
| return ' '.join(chunks) | |
| def select_matrix(records, tasks, states=range(20,25)): | |
| index={} | |
| for r in records: | |
| if r.get('condition',r.get('language')) != 'hinglish': continue | |
| key=(r['task_id'],r['initial_state_index']) | |
| if key in index: raise ValueError('Duplicate Hinglish task/state') | |
| index[key]=r | |
| try: return [index[t,s] for s in states for t in tasks] | |
| except KeyError as e: raise ValueError('Missing Hinglish matrix episode') from e | |
| def card(r, title=None, instruction=True): | |
| outcome = '' if r.get('success') is None else ('Success' if r['success'] else 'Failure') | |
| cls = 'success' if r.get('success') else 'failure' | |
| heading='<h4>'+esc(title)+'</h4>' if title else '' | |
| text='<p class="instruction">'+esc(r['instruction'])+'</p>' if instruction else '' | |
| poster=r.get('poster',r.get('initial_frame','')) | |
| full=r.get('full_recording',r['clip']) | |
| return '<article class="video-card">'+heading+text+'<div class="video-meta"><span>Task '+str(r['task_id'])+' · State '+str(r['initial_state_index'])+'</span><span class="outcome '+cls+'">'+outcome+'</span></div><video controls preload="none" playsinline poster="'+esc(poster)+'" src="'+esc(r['clip'])+'">Video playback unavailable.</video><a href="'+esc(full)+'">Full recording</a> · <a href="'+esc(poster)+'">Still image</a></article>' | |
| def video_row(records, titles=None): | |
| titles=titles or [LABELS.get(r.get('language',r.get('condition')), 'French') for r in records] | |
| return wrap('<div class="comparison media-comparison">'+''.join(card(r,t) for r,t in zip(records,titles))+'</div>') | |
| def matrix_html(records, tasks): | |
| records=select_matrix(records,tasks) | |
| heads=[] | |
| for t in tasks: | |
| r=next(r for r in records if r['task_id']==t) | |
| heads.append('<header class="matrix-heading"><strong>Task '+str(t)+': '+TASKS[t]+'</strong><p>'+esc(r['instruction'])+'</p></header>') | |
| return wrap('<div class="matrix-scroll"><div class="rollout-matrix">'+''.join(heads)+''.join(card(r,instruction=False) for r in records)+'</div></div>') | |
| def views(external,wrist,caption='Simultaneous camera observations'): | |
| return wrap('<div class="views"><figure><img src="'+esc(external)+'"><figcaption>External camera</figcaption></figure><figure><img src="'+esc(wrist)+'"><figcaption>Wrist camera</figcaption></figure></div><p class="note">'+esc(caption)+'</p>') | |
| def frame_row(record): | |
| return wrap('<div class="scroll"><div class="frames">'+''.join('<figure><img src="'+esc(p)+'"><figcaption>'+esc(label)+' · '+f'{t:.1f}s'+'</figcaption></figure>' for p,t,label in zip(record['key_frames'],record['key_frame_seconds'],record['key_frame_labels']))+'</div></div>') | |
Xet Storage Details
- Size:
- 7.33 kB
- Xet hash:
- c37182e49b5528c0e26c1f29ac49feb7046f3d25ae364914849c3a9b2b0a9cf9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.