Download src/catalog.js from DarthSoluke/EgoNetDatasetResearch: direct link, hf CLI and curl.
- Browser
- Download file 11.8 kB
-
https://huggingface.co/spaces/DarthSoluke/EgoNetDatasetResearch/resolve/main/src/catalog.js
- Command line
-
hf download hf://spaces/DarthSoluke/EgoNetDatasetResearch/src/catalog.js
-
curl -L -o catalog.js https://huggingface.co/spaces/DarthSoluke/EgoNetDatasetResearch/resolve/main/src/catalog.js
11.8 kB
| import { | |
| esc, | |
| icon, | |
| external, | |
| section, | |
| table, | |
| empty, | |
| fact, | |
| tier, | |
| status, | |
| thumbnail, | |
| enhance, | |
| } from "./ui.js"; | |
| import { | |
| previewHTML, | |
| figuresHTML, | |
| videoHTML, | |
| bindPreview, | |
| bindVideo, | |
| } from "./preview.js"; | |
| export const defaults = { | |
| q: "", | |
| source: "", | |
| method: "", | |
| tier: "", | |
| status: "", | |
| route: "", | |
| preview: false, | |
| sort: "name", | |
| }; | |
| const tabs = [ | |
| ["overview", "概况"], | |
| ["annotation", "采集与标注"], | |
| ["access", "数据访问"], | |
| ["conversion", "SOMA转换"], | |
| ]; | |
| function options(list, value, label) { | |
| return `<option value="">${label}</option>${list.map((item) => `<option value="${esc(item.id)}" ${item.id === value ? "selected" : ""}>${esc(item.name)}</option>`).join("")}`; | |
| } | |
| export function filterDatasets(context, filters) { | |
| const q = filters.q.trim().toLowerCase(); | |
| return context.data.datasets | |
| .filter((d) => { | |
| if ( | |
| ["source", "method", "tier", "status", "route"].some( | |
| (key) => filters[key] && d[key] !== filters[key], | |
| ) | |
| ) | |
| return false; | |
| if (filters.preview && !context.previews[d.id]?.length) return false; | |
| const haystack = [ | |
| d.name, | |
| d.camera, | |
| d.gt, | |
| d.fills, | |
| d.scale, | |
| d.subjects, | |
| ...(d.tags || []), | |
| ] | |
| .join(" ") | |
| .toLowerCase(); | |
| return !q || haystack.includes(q); | |
| }) | |
| .sort((a, b) => { | |
| if (filters.sort === "year") | |
| return ( | |
| (Number(b.year?.match(/\d{4}/)?.[0]) || 0) - | |
| (Number(a.year?.match(/\d{4}/)?.[0]) || 0) || | |
| a.name.localeCompare(b.name) | |
| ); | |
| if (filters.sort === "status") | |
| return ( | |
| context.meta.status[a.status].order - | |
| context.meta.status[b.status].order || a.name.localeCompare(b.name) | |
| ); | |
| return a.name.localeCompare(b.name, "en", { sensitivity: "base" }); | |
| }); | |
| } | |
| export function catalogView(context, filters) { | |
| return `<section class="view catalog-view"><header class="page-heading"><h1>数据集 <span class="heading-count">${context.data.datasets.length}</span></h1><div class="heading-actions"><a href="#overview" class="text-link"><i data-lucide="grid-2x2" aria-hidden="true"></i>数据分布</a></div></header> | |
| <form id="catalog-filters" role="search" class="catalog-filters"> | |
| <div class="search-field"><i data-lucide="search" aria-hidden="true"></i><input id="dataset-search" type="search" aria-label="搜索数据集" placeholder="搜索数据集、相机或标注" value="${esc(filters.q)}"></div> | |
| <div class="filter-row"> | |
| <select data-filter="source" aria-label="数据来源">${options(context.data.meta.sources, filters.source, "全部来源")}</select> | |
| <select data-filter="method" aria-label="标注方法">${options(context.data.meta.methods, filters.method, "全部标注")}</select> | |
| <select data-filter="tier" aria-label="Shape真值层级">${options( | |
| context.data.meta.tiers.map((t) => ({ | |
| ...t, | |
| name: `${t.id === "-" ? "—" : t.id} ${t.name}`, | |
| })), | |
| filters.tier, | |
| "全部Shape层级", | |
| )}</select> | |
| <select data-filter="status" aria-label="使用状态">${options(context.data.meta.statuses, filters.status, "全部状态")}</select> | |
| ${filters.route ? `<select data-filter="route" aria-label="SOMA转换路线">${options(context.data.meta.routes, filters.route, "全部转换路线")}</select>` : ""} | |
| <label class="checkbox"><input data-filter="preview" type="checkbox" ${filters.preview ? "checked" : ""}>有样例</label> | |
| ${icon("rotate-ccw", "重置筛选", "data-clear-filters")} | |
| </div> | |
| </form> | |
| <div class="results-toolbar"><span id="result-count" role="status" aria-live="polite"></span><select id="dataset-sort" aria-label="排序"><option value="name">名称</option><option value="year">年份</option><option value="status">使用状态</option></select></div> | |
| <div class="dataset-list"><div class="dataset-columns" aria-hidden="true"><span>数据集</span><span>数据来源</span><span>标注</span><span>规模</span><span>状态</span></div><div id="dataset-results"></div></div> | |
| </section>`; | |
| } | |
| export function bindCatalog(context, filters) { | |
| function update() { | |
| const datasets = filterDatasets(context, filters); | |
| document.getElementById("result-count").textContent = | |
| `${datasets.length} / ${context.data.datasets.length}`; | |
| document.getElementById("dataset-results").innerHTML = datasets.length | |
| ? datasets | |
| .map( | |
| (d) => `<a class="dataset-row" href="#ds-${d.id}"> | |
| <div class="dataset-name">${thumbnail(d, context)}<div><h2>${esc(d.name)}</h2><span class="dataset-year">${esc(d.year)}</span></div></div> | |
| <div class="dataset-source"><span>${esc(context.meta.source[d.source].name)}</span><span class="muted">${esc(context.meta.method[d.method].short)}</span></div> | |
| <div class="dataset-annotation"><span>${esc(d.gt)}</span>${tier(d.tier, context.meta)}</div> | |
| <div class="dataset-scale"><span>${esc(d.scale || "—")}</span><span class="muted">${esc(d.subjects)}</span></div> | |
| <div class="dataset-status">${status(d.status, context.meta)}</div> | |
| </a>`, | |
| ) | |
| .join("") | |
| : empty("没有匹配的数据集", true); | |
| const params = new URLSearchParams(); | |
| Object.entries(filters).forEach(([k, v]) => { | |
| if (v && v !== defaults[k]) params.set(k, String(v)); | |
| }); | |
| history.replaceState( | |
| null, | |
| "", | |
| `#catalog${params.size ? "?" + params : ""}`, | |
| ); | |
| document.getElementById("dataset-sort").value = filters.sort; | |
| enhance(); | |
| document.querySelectorAll("[data-clear-filters]").forEach((button) => { | |
| button.onclick = reset; | |
| }); | |
| } | |
| function reset() { | |
| Object.assign(filters, defaults); | |
| document.getElementById("dataset-search").value = ""; | |
| document.querySelectorAll("[data-filter]").forEach((el) => { | |
| if (el.type === "checkbox") el.checked = false; | |
| else el.value = ""; | |
| }); | |
| update(); | |
| } | |
| document.getElementById("catalog-filters").onsubmit = (event) => | |
| event.preventDefault(); | |
| document.getElementById("dataset-search").oninput = (event) => { | |
| filters.q = event.target.value; | |
| update(); | |
| }; | |
| document.querySelectorAll("[data-filter]").forEach((el) => { | |
| el.onchange = () => { | |
| filters[el.dataset.filter] = | |
| el.type === "checkbox" ? el.checked : el.value; | |
| update(); | |
| }; | |
| }); | |
| document.getElementById("dataset-sort").onchange = (event) => { | |
| filters.sort = event.target.value; | |
| update(); | |
| }; | |
| update(); | |
| } | |
| export function overviewView(context) { | |
| const { data, meta } = context; | |
| const matrix = data.meta.methods.map( | |
| (method) => | |
| `<tr><th scope="row"><a href="#methods/${method.id}">${esc(method.short)}</a></th>${data.meta.sources | |
| .map((source) => { | |
| const items = data.datasets.filter( | |
| (d) => d.method === method.id && d.source === source.id, | |
| ); | |
| return `<td>${items.map((d) => `<a href="#ds-${d.id}" class="matrix-link">${esc(d.name)}</a>`).join("") || '<span class="muted">—</span>'}</td>`; | |
| }) | |
| .join("")}</tr>`, | |
| ); | |
| return `<section class="view"><header class="page-heading"><h1>数据分布</h1><a href="#catalog" class="text-link">${data.datasets.length} 个数据集<i data-lucide="arrow-up-right" aria-hidden="true"></i></a></header> | |
| <div class="source-counts">${data.meta.sources.map((source) => `<a href="#catalog?source=${source.id}"><b>${data.datasets.filter((d) => d.source === source.id).length}</b><span>${esc(source.name)}</span></a>`).join("")}</div> | |
| ${section("来源 × 标注", table(["标注方法", ...data.meta.sources.map((s) => s.name)], matrix, "matrix"))} | |
| ${section("Shape真值", `<dl class="tier-reference">${data.meta.tiers.map((t) => `<div><dt>${tier(t.id, meta)}<strong>${esc(t.name)}</strong></dt><dd>${esc(t.desc)}</dd><a href="#catalog?tier=${encodeURIComponent(t.id)}">${data.datasets.filter((d) => d.tier === t.id).length}<i data-lucide="arrow-up-right" aria-hidden="true"></i></a></div>`).join("")}</dl>`)} | |
| ${section( | |
| "优先接入", | |
| `<div class="recommendations">${data.datasets | |
| .filter((d) => d.status === "p0") | |
| .map( | |
| (d) => | |
| `<a href="#ds-${d.id}">${thumbnail(d, context)}<h3>${esc(d.name)}</h3><p>${esc(d.fills)}</p></a>`, | |
| ) | |
| .join("")}</div>`, | |
| )} | |
| </section>`; | |
| } | |
| export function detailView(id, tab, context) { | |
| const dataset = context.byId[id]; | |
| if (!dataset) | |
| return empty("未找到这个数据集") + '<a href="#catalog">返回数据集</a>'; | |
| const { meta } = context, | |
| method = meta.method[dataset.method], | |
| route = meta.route[dataset.route]; | |
| const links = [...(dataset.links || []), ...(dataset.team || [])]; | |
| const body = | |
| tab === "annotation" | |
| ? `${section(method.name, `<ol class="flow">${method.steps.map((s) => `<li>${esc(s)}</li>`).join("")}</ol><dl class="facts">${fact("可信度", esc(method.trust))}${fact("常见问题", esc(method.issues))}</dl><a href="#methods/${method.id}" class="text-link">相关数据集<i data-lucide="arrow-up-right" aria-hidden="true"></i></a>`)}${figuresHTML(id, context)}` | |
| : tab === "access" | |
| ? section( | |
| "来源", | |
| `<ul class="resource-links">${links.map((l) => `<li>${external(l.label, l.url)}</li>`).join("")}</ul>${!links.length ? '<p class="muted">暂无已登记的访问链接。</p>' : ""}`, | |
| ) | |
| : tab === "conversion" | |
| ? `${section(route.name, `<dl class="facts">${fact("原始标注", esc(route.from))}${fact("方法", esc(route.how))}${fact("Shape监督", route.shape ? "可用;仍须核对来源与拟合残差" : "不使用")}${fact("层级", tier(dataset.tier, meta) + " " + esc(meta.tier[dataset.tier].name))}</dl><a class="text-link" href="#soma/${route.id}">全部转换路线<i data-lucide="arrow-up-right" aria-hidden="true"></i></a>`)}${id === "egorenderer" ? '<p class="notice">拟合MANO/SOMA是辅助标注,不参与原生手部渲染。</p>' : ""}` | |
| : `${previewHTML(dataset, context)}${section("数据规格", `<dl class="facts">${fact("视角与相机", esc(dataset.camera))}${fact("受试者", esc(dataset.subjects))}${fact("规模", esc(dataset.scale))}${fact("原始标注", esc(dataset.gt))}${fact("Shape来源", tier(dataset.tier, meta) + " " + esc(meta.tier[dataset.tier].name))}</dl>${dataset.risk ? `<p class="notice">${esc(dataset.risk)}</p>` : ""}`)}${videoHTML(id, context)}${!context.previews[id]?.length ? figuresHTML(id, context) : ""}`; | |
| return `<section class="view detail-view"><a class="breadcrumb" href="#catalog"><i data-lucide="arrow-left" aria-hidden="true"></i>数据集</a><header class="detail-heading"><div><h1>${esc(dataset.name)}</h1><p>${esc(dataset.fills)}</p></div><div class="detail-meta">${status(dataset.status, meta)}<span>${esc(dataset.year)}</span></div></header> | |
| ${id === "egorenderer" ? '<div class="dataset-tools"><a href="#synthetic/progress">生产进度<i data-lucide="arrow-up-right" aria-hidden="true"></i></a><a href="#synthetic/reading">读取方法<i data-lucide="arrow-up-right" aria-hidden="true"></i></a><a href="#synthetic/versions">版本<i data-lucide="arrow-up-right" aria-hidden="true"></i></a></div>' : ""} | |
| <nav class="section-tabs" aria-label="数据集详情">${tabs.map(([key, name]) => `<a href="#ds-${id}/${key}" ${key === tab ? 'aria-current="page"' : ""}>${name}</a>`).join("")}</nav>${body} | |
| </section>`; | |
| } | |
| export function bindDetail(id, context) { | |
| if (!context.byId[id]) return; | |
| bindPreview(context.byId[id], context); | |
| bindVideo(id, context); | |
| } | |