Spaces:
Running
Running
refactor - needs some more tlc
Browse files- index.html +1 -387
- js/app.js +30 -0
- js/chart-manager.js +99 -0
- js/data-fetcher.js +73 -0
- js/pixel-counter.js +181 -0
- js/progress-bar.js +21 -0
index.html
CHANGED
|
@@ -57,392 +57,6 @@
|
|
| 57 |
</div>
|
| 58 |
</div>
|
| 59 |
|
| 60 |
-
<script>
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
const REPO_CSV_URL = 'https://huggingface.co/datasets/jsulz/ready-xet-go/resolve/main/repo-progress.csv';
|
| 64 |
-
|
| 65 |
-
const FILE_CSV_URL = 'https://huggingface.co/datasets/jsulz/ready-xet-go/resolve/main/file-progress.csv';
|
| 66 |
-
|
| 67 |
-
const BYTES_CSV_URL = 'https://huggingface.co/datasets/jsulz/ready-xet-go/resolve/main/bytes-progress.csv';
|
| 68 |
-
|
| 69 |
-
(async () => {
|
| 70 |
-
/*
|
| 71 |
-
* Data fetching and parsing
|
| 72 |
-
*/
|
| 73 |
-
const repo_rows = [];
|
| 74 |
-
const file_rows = [];
|
| 75 |
-
const bytes_rows = [];
|
| 76 |
-
|
| 77 |
-
const repo_csv = await new Promise((resolve, reject) => {
|
| 78 |
-
Papa.parse(REPO_CSV_URL, {
|
| 79 |
-
download: true,
|
| 80 |
-
header : true,
|
| 81 |
-
complete: resolve,
|
| 82 |
-
error : reject
|
| 83 |
-
});
|
| 84 |
-
});
|
| 85 |
-
repo_csv.data.forEach(row => {
|
| 86 |
-
if (row.date && row.xet_repos && row.hub_repos) {
|
| 87 |
-
repo_rows.push({
|
| 88 |
-
date: new Date(row.date),
|
| 89 |
-
xet_repos: parseInt(row.xet_repos, 10),
|
| 90 |
-
hub_repos: parseInt(row.hub_repos, 10)
|
| 91 |
-
});
|
| 92 |
-
}
|
| 93 |
-
});
|
| 94 |
-
|
| 95 |
-
const file_csv = await new Promise((resolve, reject) => {
|
| 96 |
-
Papa.parse(FILE_CSV_URL, {
|
| 97 |
-
download: true,
|
| 98 |
-
header : true,
|
| 99 |
-
complete: resolve,
|
| 100 |
-
error : reject
|
| 101 |
-
});
|
| 102 |
-
});
|
| 103 |
-
file_csv.data.forEach(row => {
|
| 104 |
-
if (row.date && row.lfs_files && row.xet_files) {
|
| 105 |
-
file_rows.push({
|
| 106 |
-
date: new Date(row.date),
|
| 107 |
-
lfs_files: parseInt(row.lfs_files, 10),
|
| 108 |
-
xet_files: parseInt(row.xet_files, 10)
|
| 109 |
-
});
|
| 110 |
-
}
|
| 111 |
-
});
|
| 112 |
-
|
| 113 |
-
const bytes_csv = await new Promise((resolve, reject) => {
|
| 114 |
-
Papa.parse(BYTES_CSV_URL, {
|
| 115 |
-
download: true,
|
| 116 |
-
header : true,
|
| 117 |
-
complete: resolve,
|
| 118 |
-
error : reject
|
| 119 |
-
});
|
| 120 |
-
});
|
| 121 |
-
bytes_csv.data.forEach(row => {
|
| 122 |
-
if (row.date && row.bytes) {
|
| 123 |
-
bytes_rows.push({
|
| 124 |
-
date: new Date(row.date),
|
| 125 |
-
bytes: parseInt(row.bytes, 10)
|
| 126 |
-
});
|
| 127 |
-
}
|
| 128 |
-
});
|
| 129 |
-
|
| 130 |
-
/*
|
| 131 |
-
* Progress Bar
|
| 132 |
-
*/
|
| 133 |
-
// Progress Bar Constants
|
| 134 |
-
const max_value = 100;
|
| 135 |
-
const current_status = Math.round((repo_rows[repo_rows.length - 1].xet_repos / repo_rows[repo_rows.length - 1].hub_repos) * max_value);
|
| 136 |
-
// Animate Progress Bar - increments smoothly every 50ms
|
| 137 |
-
const progressBar = document.getElementById('progressBar');
|
| 138 |
-
let currentProgress = 0;
|
| 139 |
-
|
| 140 |
-
function animateProgressBar() {
|
| 141 |
-
if (currentProgress < current_status) {
|
| 142 |
-
currentProgress++;
|
| 143 |
-
progressBar.style.width = `${currentProgress}%`;
|
| 144 |
-
progressBar.textContent = `${currentProgress}%`;
|
| 145 |
-
setTimeout(animateProgressBar, 75);
|
| 146 |
-
}
|
| 147 |
-
}
|
| 148 |
-
animateProgressBar();
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
/*
|
| 152 |
-
* Charts
|
| 153 |
-
*/
|
| 154 |
-
|
| 155 |
-
//
|
| 156 |
-
// Line chart for repo migration progress
|
| 157 |
-
//
|
| 158 |
-
|
| 159 |
-
// Set up chart data
|
| 160 |
-
const maxCount = Math.max(
|
| 161 |
-
...repo_rows.map(row => row.xet_repos),
|
| 162 |
-
);
|
| 163 |
-
const dataPoints = repo_rows.map(row => ({
|
| 164 |
-
date: row.date,
|
| 165 |
-
value: row.xet_repos
|
| 166 |
-
}));
|
| 167 |
-
|
| 168 |
-
// Format date for display (MMM D YYYY)
|
| 169 |
-
function formatDate(dateStr) {
|
| 170 |
-
return new Date(dateStr).toLocaleDateString('en-US', {
|
| 171 |
-
year: 'numeric', month: 'short', day: 'numeric'
|
| 172 |
-
});
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
// Initialize line chart
|
| 176 |
-
const lineChartCtx = document.getElementById('progressChart').getContext('2d');
|
| 177 |
-
let lineChart = new Chart(lineChartCtx, {
|
| 178 |
-
type: 'line',
|
| 179 |
-
data: {
|
| 180 |
-
labels: dataPoints.map(point => formatDate(point.date)),
|
| 181 |
-
datasets: [{
|
| 182 |
-
label: 'Repos Migrated',
|
| 183 |
-
data: dataPoints.map(point => point.value),
|
| 184 |
-
borderColor: '#7875FF',
|
| 185 |
-
backgroundColor: 'rgba(255, 127, 65, 1)',
|
| 186 |
-
tension: 0.4
|
| 187 |
-
}]
|
| 188 |
-
},
|
| 189 |
-
options: {
|
| 190 |
-
responsive: true,
|
| 191 |
-
maintainAspectRatio: false,
|
| 192 |
-
animation: { duration: 2000 },
|
| 193 |
-
scales: {
|
| 194 |
-
y: {
|
| 195 |
-
min: 0,
|
| 196 |
-
ticks: { stepSize: 100000 }
|
| 197 |
-
},
|
| 198 |
-
x: {
|
| 199 |
-
ticks: {
|
| 200 |
-
maxTicksLimit: 20
|
| 201 |
-
}
|
| 202 |
-
},
|
| 203 |
-
},
|
| 204 |
-
plugins: {
|
| 205 |
-
tooltip: {
|
| 206 |
-
callbacks: {
|
| 207 |
-
label: function(context) {
|
| 208 |
-
return `Repos Migrated: ${context.parsed.y.toLocaleString()}`;
|
| 209 |
-
}
|
| 210 |
-
}
|
| 211 |
-
}
|
| 212 |
-
}
|
| 213 |
-
}
|
| 214 |
-
});
|
| 215 |
-
|
| 216 |
-
//
|
| 217 |
-
// Donut chart for large files
|
| 218 |
-
//
|
| 219 |
-
|
| 220 |
-
// Set up donut chart data
|
| 221 |
-
const lfs_file_count = file_rows[file_rows.length - 1].lfs_files;
|
| 222 |
-
const xet_file_count = file_rows[file_rows.length - 1].xet_files;
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
// Initialize donut chart
|
| 226 |
-
const donutChartCtx = document.getElementById('largeFilesChart').getContext('2d');
|
| 227 |
-
let donutChart = new Chart(donutChartCtx, {
|
| 228 |
-
type: 'doughnut',
|
| 229 |
-
data: {
|
| 230 |
-
labels: [
|
| 231 |
-
'LFS Files',
|
| 232 |
-
'Xet Files',
|
| 233 |
-
],
|
| 234 |
-
datasets: [{
|
| 235 |
-
label: 'My First Dataset',
|
| 236 |
-
data: [lfs_file_count, xet_file_count],
|
| 237 |
-
backgroundColor: [
|
| 238 |
-
'oklch(0.577 0.245 27.325 / 75.56%)',
|
| 239 |
-
'oklch(0.6361 0.1994 280.07 / 71.37%)',
|
| 240 |
-
],
|
| 241 |
-
hoverOffset: 4
|
| 242 |
-
}]
|
| 243 |
-
},
|
| 244 |
-
options: {
|
| 245 |
-
responsive: true,
|
| 246 |
-
maintainAspectRatio: false,
|
| 247 |
-
animation: { duration: 2000 },
|
| 248 |
-
plugins: {
|
| 249 |
-
legend: {
|
| 250 |
-
position: 'top',
|
| 251 |
-
},
|
| 252 |
-
}
|
| 253 |
-
}
|
| 254 |
-
});
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
/*
|
| 260 |
-
* Counter
|
| 261 |
-
*/
|
| 262 |
-
|
| 263 |
-
// 5x7 pixel patterns for each character (1=pixel on, 0=pixel off)
|
| 264 |
-
const patterns = {
|
| 265 |
-
'1': [
|
| 266 |
-
'00100',
|
| 267 |
-
'01100',
|
| 268 |
-
'00100',
|
| 269 |
-
'00100',
|
| 270 |
-
'00100',
|
| 271 |
-
'00100',
|
| 272 |
-
'01110'
|
| 273 |
-
],
|
| 274 |
-
'2': [
|
| 275 |
-
'01110',
|
| 276 |
-
'10001',
|
| 277 |
-
'00001',
|
| 278 |
-
'00010',
|
| 279 |
-
'00100',
|
| 280 |
-
'01000',
|
| 281 |
-
'11111'
|
| 282 |
-
],
|
| 283 |
-
'3': [
|
| 284 |
-
'01110',
|
| 285 |
-
'10001',
|
| 286 |
-
'00001',
|
| 287 |
-
'00110',
|
| 288 |
-
'00001',
|
| 289 |
-
'10001',
|
| 290 |
-
'01110'
|
| 291 |
-
],
|
| 292 |
-
'4': [
|
| 293 |
-
'00010',
|
| 294 |
-
'00110',
|
| 295 |
-
'01010',
|
| 296 |
-
'10010',
|
| 297 |
-
'11111',
|
| 298 |
-
'00010',
|
| 299 |
-
'00010'
|
| 300 |
-
],
|
| 301 |
-
'5': [
|
| 302 |
-
'11111',
|
| 303 |
-
'10000',
|
| 304 |
-
'10000',
|
| 305 |
-
'11110',
|
| 306 |
-
'00001',
|
| 307 |
-
'10001',
|
| 308 |
-
'01110'
|
| 309 |
-
],
|
| 310 |
-
'6': [
|
| 311 |
-
'01110',
|
| 312 |
-
'10001',
|
| 313 |
-
'10000',
|
| 314 |
-
'11110',
|
| 315 |
-
'10001',
|
| 316 |
-
'10001',
|
| 317 |
-
'01110'
|
| 318 |
-
],
|
| 319 |
-
'7': [
|
| 320 |
-
'11111',
|
| 321 |
-
'00001',
|
| 322 |
-
'00010',
|
| 323 |
-
'00100',
|
| 324 |
-
'01000',
|
| 325 |
-
'01000',
|
| 326 |
-
'01000'
|
| 327 |
-
],
|
| 328 |
-
'8': [
|
| 329 |
-
'01110',
|
| 330 |
-
'10001',
|
| 331 |
-
'10001',
|
| 332 |
-
'01110',
|
| 333 |
-
'10001',
|
| 334 |
-
'10001',
|
| 335 |
-
'01110'
|
| 336 |
-
],
|
| 337 |
-
'9': [
|
| 338 |
-
'01110',
|
| 339 |
-
'10001',
|
| 340 |
-
'10001',
|
| 341 |
-
'01111',
|
| 342 |
-
'00001',
|
| 343 |
-
'10001',
|
| 344 |
-
'01110'
|
| 345 |
-
],
|
| 346 |
-
'0': [
|
| 347 |
-
'01110',
|
| 348 |
-
'10001',
|
| 349 |
-
'10001',
|
| 350 |
-
'10001',
|
| 351 |
-
'10001',
|
| 352 |
-
'10001',
|
| 353 |
-
'01110'
|
| 354 |
-
],
|
| 355 |
-
'.': [
|
| 356 |
-
'00000',
|
| 357 |
-
'00000',
|
| 358 |
-
'00000',
|
| 359 |
-
'00000',
|
| 360 |
-
'00000',
|
| 361 |
-
'00100',
|
| 362 |
-
'01110'
|
| 363 |
-
],
|
| 364 |
-
'P': [
|
| 365 |
-
'11110',
|
| 366 |
-
'10001',
|
| 367 |
-
'10001',
|
| 368 |
-
'11110',
|
| 369 |
-
'10000',
|
| 370 |
-
'10000',
|
| 371 |
-
'10000'
|
| 372 |
-
],
|
| 373 |
-
'B': [
|
| 374 |
-
'11110',
|
| 375 |
-
'10001',
|
| 376 |
-
'10001',
|
| 377 |
-
'11110',
|
| 378 |
-
'10001',
|
| 379 |
-
'10001',
|
| 380 |
-
'11110'
|
| 381 |
-
]
|
| 382 |
-
};
|
| 383 |
-
|
| 384 |
-
console.log(bytes_rows[bytes_rows.length - 1].bytes)
|
| 385 |
-
// Convert bytes to PB and format as string
|
| 386 |
-
const bytesInPB = 1024 ** 5;
|
| 387 |
-
// Calculate the number of PBs and round to 2 decimal places
|
| 388 |
-
const text = (bytes_rows[bytes_rows.length - 1].bytes / bytesInPB).toFixed(2) + 'PB';
|
| 389 |
-
console.log(text)
|
| 390 |
-
const container = document.getElementById('counter');
|
| 391 |
-
const pixelSize = 5; // Match CSS variable
|
| 392 |
-
const pixelGap = 1;
|
| 393 |
-
let allPixels = [];
|
| 394 |
-
|
| 395 |
-
// Create characters and their pixels
|
| 396 |
-
text.split('').forEach((char, charIndex) => {
|
| 397 |
-
const charDiv = document.createElement('div');
|
| 398 |
-
charDiv.className = 'character';
|
| 399 |
-
|
| 400 |
-
const pattern = patterns[char];
|
| 401 |
-
if (!pattern) return;
|
| 402 |
-
|
| 403 |
-
// Generate pixels based on pattern
|
| 404 |
-
pattern.forEach((row, rowIndex) => {
|
| 405 |
-
row.split('').forEach((cell, colIndex) => {
|
| 406 |
-
if (cell === '1') {
|
| 407 |
-
const pixel = document.createElement('div');
|
| 408 |
-
pixel.className = 'pixel';
|
| 409 |
-
|
| 410 |
-
// Calculate final position within character grid
|
| 411 |
-
const finalX = colIndex * (pixelSize + pixelGap);
|
| 412 |
-
const finalY = rowIndex * (pixelSize + pixelGap);
|
| 413 |
-
|
| 414 |
-
pixel.style.left = `${finalX}px`;
|
| 415 |
-
pixel.style.top = `${finalY}px`;
|
| 416 |
-
|
| 417 |
-
charDiv.appendChild(pixel);
|
| 418 |
-
allPixels.push(pixel);
|
| 419 |
-
}
|
| 420 |
-
});
|
| 421 |
-
});
|
| 422 |
-
|
| 423 |
-
container.appendChild(charDiv);
|
| 424 |
-
});
|
| 425 |
-
|
| 426 |
-
console.log(allPixels)
|
| 427 |
-
|
| 428 |
-
// Animate pixels falling into their final positions
|
| 429 |
-
allPixels.forEach((pixel, index) => {
|
| 430 |
-
// Stagger the landing times for a cascading effect
|
| 431 |
-
const delay = Math.random() * 50 + (index * 50);
|
| 432 |
-
|
| 433 |
-
setTimeout(() => {
|
| 434 |
-
pixel.classList.add('landed');
|
| 435 |
-
}, delay);
|
| 436 |
-
});
|
| 437 |
-
const maxDelay = Math.max(...allPixels.map((_, index) => Math.random() * 50 + (index * 50)));
|
| 438 |
-
setTimeout(() => {
|
| 439 |
-
const textDiv = document.getElementById('counterText');
|
| 440 |
-
textDiv.style.opacity = '1';
|
| 441 |
-
}, maxDelay + 500);
|
| 442 |
-
|
| 443 |
-
})();
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
</script>
|
| 447 |
</body>
|
| 448 |
</html>
|
|
|
|
| 57 |
</div>
|
| 58 |
</div>
|
| 59 |
|
| 60 |
+
<script type="module" src="js/app.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
</body>
|
| 62 |
</html>
|
js/app.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { DataFetcher } from './data-fetcher.js';
|
| 2 |
+
import { ProgressBar } from './progress-bar.js';
|
| 3 |
+
import { ChartManager } from './chart-manager.js';
|
| 4 |
+
import { PixelCounter } from './pixel-counter.js';
|
| 5 |
+
|
| 6 |
+
class App {
|
| 7 |
+
constructor() {
|
| 8 |
+
this.dataFetcher = new DataFetcher();
|
| 9 |
+
this.progressBar = new ProgressBar('progressBar');
|
| 10 |
+
this.chartManager = new ChartManager();
|
| 11 |
+
this.pixelCounter = new PixelCounter();
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
async init() {
|
| 15 |
+
try {
|
| 16 |
+
const data = await this.dataFetcher.fetchAllData();
|
| 17 |
+
|
| 18 |
+
this.progressBar.init(data.repoData);
|
| 19 |
+
this.chartManager.init(data.repoData, data.fileData);
|
| 20 |
+
this.pixelCounter.init(data.bytesData);
|
| 21 |
+
} catch (error) {
|
| 22 |
+
console.error('Failed to initialize app:', error);
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
(async () => {
|
| 28 |
+
const app = new App();
|
| 29 |
+
await app.init();
|
| 30 |
+
})();
|
js/chart-manager.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export class ChartManager {
|
| 2 |
+
constructor() {
|
| 3 |
+
this.charts = {};
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
formatDate(dateStr) {
|
| 7 |
+
return new Date(dateStr).toLocaleDateString('en-US', {
|
| 8 |
+
year: 'numeric', month: 'short', day: 'numeric'
|
| 9 |
+
});
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
createProgressChart(repoData) {
|
| 13 |
+
const maxCount = Math.max(...repoData.map(row => row.xet_repos));
|
| 14 |
+
const dataPoints = repoData.map(row => ({
|
| 15 |
+
date: row.date,
|
| 16 |
+
value: row.xet_repos
|
| 17 |
+
}));
|
| 18 |
+
|
| 19 |
+
const lineChartCtx = document.getElementById('progressChart').getContext('2d');
|
| 20 |
+
this.charts.lineChart = new Chart(lineChartCtx, {
|
| 21 |
+
type: 'line',
|
| 22 |
+
data: {
|
| 23 |
+
labels: dataPoints.map(point => this.formatDate(point.date)),
|
| 24 |
+
datasets: [{
|
| 25 |
+
label: 'Repos Migrated',
|
| 26 |
+
data: dataPoints.map(point => point.value),
|
| 27 |
+
borderColor: '#7875FF',
|
| 28 |
+
backgroundColor: 'rgba(255, 127, 65, 1)',
|
| 29 |
+
tension: 0.4
|
| 30 |
+
}]
|
| 31 |
+
},
|
| 32 |
+
options: {
|
| 33 |
+
responsive: true,
|
| 34 |
+
maintainAspectRatio: false,
|
| 35 |
+
animation: { duration: 2000 },
|
| 36 |
+
scales: {
|
| 37 |
+
y: {
|
| 38 |
+
min: 0,
|
| 39 |
+
ticks: { stepSize: 100000 }
|
| 40 |
+
},
|
| 41 |
+
x: {
|
| 42 |
+
ticks: {
|
| 43 |
+
maxTicksLimit: 20
|
| 44 |
+
}
|
| 45 |
+
},
|
| 46 |
+
},
|
| 47 |
+
plugins: {
|
| 48 |
+
tooltip: {
|
| 49 |
+
callbacks: {
|
| 50 |
+
label: function(context) {
|
| 51 |
+
return `Repos Migrated: ${context.parsed.y.toLocaleString()}`;
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
});
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
createDonutChart(fileData) {
|
| 61 |
+
const lfs_file_count = fileData[fileData.length - 1].lfs_files;
|
| 62 |
+
const xet_file_count = fileData[fileData.length - 1].xet_files;
|
| 63 |
+
|
| 64 |
+
const donutChartCtx = document.getElementById('largeFilesChart').getContext('2d');
|
| 65 |
+
this.charts.donutChart = new Chart(donutChartCtx, {
|
| 66 |
+
type: 'doughnut',
|
| 67 |
+
data: {
|
| 68 |
+
labels: [
|
| 69 |
+
'LFS Files',
|
| 70 |
+
'Xet Files',
|
| 71 |
+
],
|
| 72 |
+
datasets: [{
|
| 73 |
+
label: 'My First Dataset',
|
| 74 |
+
data: [lfs_file_count, xet_file_count],
|
| 75 |
+
backgroundColor: [
|
| 76 |
+
'oklch(0.577 0.245 27.325 / 75.56%)',
|
| 77 |
+
'oklch(0.6361 0.1994 280.07 / 71.37%)',
|
| 78 |
+
],
|
| 79 |
+
hoverOffset: 4
|
| 80 |
+
}]
|
| 81 |
+
},
|
| 82 |
+
options: {
|
| 83 |
+
responsive: true,
|
| 84 |
+
maintainAspectRatio: false,
|
| 85 |
+
animation: { duration: 2000 },
|
| 86 |
+
plugins: {
|
| 87 |
+
legend: {
|
| 88 |
+
position: 'top',
|
| 89 |
+
},
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
});
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
init(repoData, fileData) {
|
| 96 |
+
this.createProgressChart(repoData);
|
| 97 |
+
this.createDonutChart(fileData);
|
| 98 |
+
}
|
| 99 |
+
}
|
js/data-fetcher.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export class DataFetcher {
|
| 2 |
+
constructor() {
|
| 3 |
+
this.REPO_CSV_URL = 'https://huggingface.co/datasets/jsulz/ready-xet-go/resolve/main/repo-progress.csv';
|
| 4 |
+
this.FILE_CSV_URL = 'https://huggingface.co/datasets/jsulz/ready-xet-go/resolve/main/file-progress.csv';
|
| 5 |
+
this.BYTES_CSV_URL = 'https://huggingface.co/datasets/jsulz/ready-xet-go/resolve/main/bytes-progress.csv';
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
async fetchCSV(url) {
|
| 9 |
+
return new Promise((resolve, reject) => {
|
| 10 |
+
Papa.parse(url, {
|
| 11 |
+
download: true,
|
| 12 |
+
header: true,
|
| 13 |
+
complete: resolve,
|
| 14 |
+
error: reject
|
| 15 |
+
});
|
| 16 |
+
});
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
parseRepoData(data) {
|
| 20 |
+
const rows = [];
|
| 21 |
+
data.forEach(row => {
|
| 22 |
+
if (row.date && row.xet_repos && row.hub_repos) {
|
| 23 |
+
rows.push({
|
| 24 |
+
date: new Date(row.date),
|
| 25 |
+
xet_repos: parseInt(row.xet_repos, 10),
|
| 26 |
+
hub_repos: parseInt(row.hub_repos, 10)
|
| 27 |
+
});
|
| 28 |
+
}
|
| 29 |
+
});
|
| 30 |
+
return rows;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
parseFileData(data) {
|
| 34 |
+
const rows = [];
|
| 35 |
+
data.forEach(row => {
|
| 36 |
+
if (row.date && row.lfs_files && row.xet_files) {
|
| 37 |
+
rows.push({
|
| 38 |
+
date: new Date(row.date),
|
| 39 |
+
lfs_files: parseInt(row.lfs_files, 10),
|
| 40 |
+
xet_files: parseInt(row.xet_files, 10)
|
| 41 |
+
});
|
| 42 |
+
}
|
| 43 |
+
});
|
| 44 |
+
return rows;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
parseBytesData(data) {
|
| 48 |
+
const rows = [];
|
| 49 |
+
data.forEach(row => {
|
| 50 |
+
if (row.date && row.bytes) {
|
| 51 |
+
rows.push({
|
| 52 |
+
date: new Date(row.date),
|
| 53 |
+
bytes: parseInt(row.bytes, 10)
|
| 54 |
+
});
|
| 55 |
+
}
|
| 56 |
+
});
|
| 57 |
+
return rows;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
async fetchAllData() {
|
| 61 |
+
const [repo_csv, file_csv, bytes_csv] = await Promise.all([
|
| 62 |
+
this.fetchCSV(this.REPO_CSV_URL),
|
| 63 |
+
this.fetchCSV(this.FILE_CSV_URL),
|
| 64 |
+
this.fetchCSV(this.BYTES_CSV_URL)
|
| 65 |
+
]);
|
| 66 |
+
|
| 67 |
+
return {
|
| 68 |
+
repoData: this.parseRepoData(repo_csv.data),
|
| 69 |
+
fileData: this.parseFileData(file_csv.data),
|
| 70 |
+
bytesData: this.parseBytesData(bytes_csv.data)
|
| 71 |
+
};
|
| 72 |
+
}
|
| 73 |
+
}
|
js/pixel-counter.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export class PixelCounter {
|
| 2 |
+
constructor() {
|
| 3 |
+
this.patterns = {
|
| 4 |
+
'1': [
|
| 5 |
+
'00100',
|
| 6 |
+
'01100',
|
| 7 |
+
'00100',
|
| 8 |
+
'00100',
|
| 9 |
+
'00100',
|
| 10 |
+
'00100',
|
| 11 |
+
'01110'
|
| 12 |
+
],
|
| 13 |
+
'2': [
|
| 14 |
+
'01110',
|
| 15 |
+
'10001',
|
| 16 |
+
'00001',
|
| 17 |
+
'00010',
|
| 18 |
+
'00100',
|
| 19 |
+
'01000',
|
| 20 |
+
'11111'
|
| 21 |
+
],
|
| 22 |
+
'3': [
|
| 23 |
+
'01110',
|
| 24 |
+
'10001',
|
| 25 |
+
'00001',
|
| 26 |
+
'00110',
|
| 27 |
+
'00001',
|
| 28 |
+
'10001',
|
| 29 |
+
'01110'
|
| 30 |
+
],
|
| 31 |
+
'4': [
|
| 32 |
+
'00010',
|
| 33 |
+
'00110',
|
| 34 |
+
'01010',
|
| 35 |
+
'10010',
|
| 36 |
+
'11111',
|
| 37 |
+
'00010',
|
| 38 |
+
'00010'
|
| 39 |
+
],
|
| 40 |
+
'5': [
|
| 41 |
+
'11111',
|
| 42 |
+
'10000',
|
| 43 |
+
'10000',
|
| 44 |
+
'11110',
|
| 45 |
+
'00001',
|
| 46 |
+
'10001',
|
| 47 |
+
'01110'
|
| 48 |
+
],
|
| 49 |
+
'6': [
|
| 50 |
+
'01110',
|
| 51 |
+
'10001',
|
| 52 |
+
'10000',
|
| 53 |
+
'11110',
|
| 54 |
+
'10001',
|
| 55 |
+
'10001',
|
| 56 |
+
'01110'
|
| 57 |
+
],
|
| 58 |
+
'7': [
|
| 59 |
+
'11111',
|
| 60 |
+
'00001',
|
| 61 |
+
'00010',
|
| 62 |
+
'00100',
|
| 63 |
+
'01000',
|
| 64 |
+
'01000',
|
| 65 |
+
'01000'
|
| 66 |
+
],
|
| 67 |
+
'8': [
|
| 68 |
+
'01110',
|
| 69 |
+
'10001',
|
| 70 |
+
'10001',
|
| 71 |
+
'01110',
|
| 72 |
+
'10001',
|
| 73 |
+
'10001',
|
| 74 |
+
'01110'
|
| 75 |
+
],
|
| 76 |
+
'9': [
|
| 77 |
+
'01110',
|
| 78 |
+
'10001',
|
| 79 |
+
'10001',
|
| 80 |
+
'01111',
|
| 81 |
+
'00001',
|
| 82 |
+
'10001',
|
| 83 |
+
'01110'
|
| 84 |
+
],
|
| 85 |
+
'0': [
|
| 86 |
+
'01110',
|
| 87 |
+
'10001',
|
| 88 |
+
'10001',
|
| 89 |
+
'10001',
|
| 90 |
+
'10001',
|
| 91 |
+
'10001',
|
| 92 |
+
'01110'
|
| 93 |
+
],
|
| 94 |
+
'.': [
|
| 95 |
+
'00000',
|
| 96 |
+
'00000',
|
| 97 |
+
'00000',
|
| 98 |
+
'00000',
|
| 99 |
+
'00000',
|
| 100 |
+
'00100',
|
| 101 |
+
'01110'
|
| 102 |
+
],
|
| 103 |
+
'P': [
|
| 104 |
+
'11110',
|
| 105 |
+
'10001',
|
| 106 |
+
'10001',
|
| 107 |
+
'11110',
|
| 108 |
+
'10000',
|
| 109 |
+
'10000',
|
| 110 |
+
'10000'
|
| 111 |
+
],
|
| 112 |
+
'B': [
|
| 113 |
+
'11110',
|
| 114 |
+
'10001',
|
| 115 |
+
'10001',
|
| 116 |
+
'11110',
|
| 117 |
+
'10001',
|
| 118 |
+
'10001',
|
| 119 |
+
'11110'
|
| 120 |
+
]
|
| 121 |
+
};
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
createCounter(bytesData) {
|
| 125 |
+
console.log(bytesData[bytesData.length - 1].bytes);
|
| 126 |
+
const bytesInPB = 1024 ** 5;
|
| 127 |
+
const text = (bytesData[bytesData.length - 1].bytes / bytesInPB).toFixed(2) + 'PB';
|
| 128 |
+
console.log(text);
|
| 129 |
+
const container = document.getElementById('counter');
|
| 130 |
+
const pixelSize = 5;
|
| 131 |
+
const pixelGap = 1;
|
| 132 |
+
let allPixels = [];
|
| 133 |
+
|
| 134 |
+
text.split('').forEach((char, charIndex) => {
|
| 135 |
+
const charDiv = document.createElement('div');
|
| 136 |
+
charDiv.className = 'character';
|
| 137 |
+
|
| 138 |
+
const pattern = this.patterns[char];
|
| 139 |
+
if (!pattern) return;
|
| 140 |
+
|
| 141 |
+
pattern.forEach((row, rowIndex) => {
|
| 142 |
+
row.split('').forEach((cell, colIndex) => {
|
| 143 |
+
if (cell === '1') {
|
| 144 |
+
const pixel = document.createElement('div');
|
| 145 |
+
pixel.className = 'pixel';
|
| 146 |
+
|
| 147 |
+
const finalX = colIndex * (pixelSize + pixelGap);
|
| 148 |
+
const finalY = rowIndex * (pixelSize + pixelGap);
|
| 149 |
+
|
| 150 |
+
pixel.style.left = `${finalX}px`;
|
| 151 |
+
pixel.style.top = `${finalY}px`;
|
| 152 |
+
|
| 153 |
+
charDiv.appendChild(pixel);
|
| 154 |
+
allPixels.push(pixel);
|
| 155 |
+
}
|
| 156 |
+
});
|
| 157 |
+
});
|
| 158 |
+
|
| 159 |
+
container.appendChild(charDiv);
|
| 160 |
+
});
|
| 161 |
+
|
| 162 |
+
console.log(allPixels);
|
| 163 |
+
|
| 164 |
+
allPixels.forEach((pixel, index) => {
|
| 165 |
+
const delay = Math.random() * 50 + (index * 50);
|
| 166 |
+
|
| 167 |
+
setTimeout(() => {
|
| 168 |
+
pixel.classList.add('landed');
|
| 169 |
+
}, delay);
|
| 170 |
+
});
|
| 171 |
+
const maxDelay = Math.max(...allPixels.map((_, index) => Math.random() * 50 + (index * 50)));
|
| 172 |
+
setTimeout(() => {
|
| 173 |
+
const textDiv = document.getElementById('counterText');
|
| 174 |
+
textDiv.style.opacity = '1';
|
| 175 |
+
}, maxDelay + 500);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
init(bytesData) {
|
| 179 |
+
this.createCounter(bytesData);
|
| 180 |
+
}
|
| 181 |
+
}
|
js/progress-bar.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export class ProgressBar {
|
| 2 |
+
constructor(elementId) {
|
| 3 |
+
this.element = document.getElementById(elementId);
|
| 4 |
+
this.currentProgress = 0;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
animate(targetProgress) {
|
| 8 |
+
if (this.currentProgress < targetProgress) {
|
| 9 |
+
this.currentProgress++;
|
| 10 |
+
this.element.style.width = `${this.currentProgress}%`;
|
| 11 |
+
this.element.textContent = `${this.currentProgress}%`;
|
| 12 |
+
setTimeout(() => this.animate(targetProgress), 75);
|
| 13 |
+
}
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
init(repoData) {
|
| 17 |
+
const max_value = 100;
|
| 18 |
+
const current_status = Math.round((repoData[repoData.length - 1].xet_repos / repoData[repoData.length - 1].hub_repos) * max_value);
|
| 19 |
+
this.animate(current_status);
|
| 20 |
+
}
|
| 21 |
+
}
|