feat: add ELO refresh job to GitLab CI and polish mobile display

- Add refresh-elo job to .gitlab-ci.yml triggered by schedules; runs
  the Python scraper and commits updated elo-data.json back to main
- Hide Stat-Check ELO column header on mobile (sm:hidden), fold ELO
  score as a subtitle under ITC Points on mobile instead

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 00:07:41 -05:00
co-authored by Claude Sonnet 4.6
parent ea0c2f3a91
commit c4e355dd63
3 changed files with 29 additions and 9 deletions
+20
View File
@@ -1,6 +1,7 @@
stages: stages:
- build - build
- deploy - deploy
- refresh
build: build:
stage: build stage: build
@@ -23,3 +24,22 @@ deploy:
environment: environment:
name: production name: production
url: https://40k-rankings.gateway-gamers.net url: https://40k-rankings.gateway-gamers.net
refresh-elo:
stage: refresh
image: python:3.12-slim
variables:
GIT_STRATEGY: clone
script:
- cd statcheck-elo-scraper && python refresh_elo.py && cd ..
- git config user.email "gitlab-ci@gateway-gamers.net"
- git config user.name "GitLab CI"
- git remote set-url origin "https://oauth2:${GITLAB_ACCESS_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
- git add public/elo-data.json
- |
if ! git diff --cached --quiet; then
git commit -m "data: refresh stat-check ELO rankings [skip ci]"
git push origin HEAD:main
fi
only:
- schedules
+1 -1
View File
@@ -76,7 +76,7 @@
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="wlt">WLT</th> <th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="wlt">WLT</th>
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="wpct">Win %</th> <th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="wpct">Win %</th>
<th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="rank">ITC Rank</th> <th class="text-right hidden sm:table-cell cursor-pointer select-none" data-sort="rank">ITC Rank</th>
<th class="text-center cursor-pointer select-none" data-sort="elo">Stat-Check ELO</th> <th class="text-center hidden sm:table-cell cursor-pointer select-none" data-sort="elo">Stat-Check ELO</th>
</tr> </tr>
</thead> </thead>
<tbody id="rows"></tbody> <tbody id="rows"></tbody>
+8 -8
View File
@@ -304,6 +304,8 @@ function render(state) {
// Rows // Rows
applySort(kept).forEach((r, i) => { applySort(kept).forEach((r, i) => {
const u = r.user || {}, name = fullName(u) || u.nickname || "Unknown"; const u = r.user || {}, name = fullName(u) || u.nickname || "Unknown";
const uid = r.userId || r.user?.id;
const eloMatch = findElo(name, uid);
const tr = document.createElement("tr"); const tr = document.createElement("tr");
tr.innerHTML = tr.innerHTML =
`<td class="font-mono text-base-content/60">${i === 0 ? `<strong class="text-base-content">1</strong>` : (i + 1)}</td>` + `<td class="font-mono text-base-content/60">${i === 0 ? `<strong class="text-base-content">1</strong>` : (i + 1)}</td>` +
@@ -312,7 +314,6 @@ function render(state) {
`<span class="block text-xs opacity-40 sm:hidden"><span class="text-success">${r.wins || 0}</span><span class="text-error">${r.losses || 0}</span>${r.ties || 0} · ITC #${r.placing ?? "—"}</span>` + `<span class="block text-xs opacity-40 sm:hidden"><span class="text-success">${r.wins || 0}</span><span class="text-error">${r.losses || 0}</span>${r.ties || 0} · ITC #${r.placing ?? "—"}</span>` +
`</td>` + `</td>` +
(() => { (() => {
const uid = r.userId || r.user?.id;
const d = uid && careerData[uid]; const d = uid && careerData[uid];
const careerSub = d == null const careerSub = d == null
? `<span class="block text-xs opacity-20">…</span>` ? `<span class="block text-xs opacity-20">…</span>`
@@ -323,8 +324,9 @@ function render(state) {
: (d.w + d.l + d.t) === 0 ? '' : (d.w + d.l + d.t) === 0 ? ''
: `<span class="block text-xs opacity-40">${fmtWpct(d.w, d.l, d.t)} career</span>`; : `<span class="block text-xs opacity-40">${fmtWpct(d.w, d.l, d.t)} career</span>`;
const mobilePct = `<span class="block text-xs opacity-40 sm:hidden">${fmtWpct(r.wins, r.losses, r.ties)} win rate</span>`; const mobilePct = `<span class="block text-xs opacity-40 sm:hidden">${fmtWpct(r.wins, r.losses, r.ties)} win rate</span>`;
const mobileElo = eloMatch ? `<span class="block text-xs opacity-40 sm:hidden">ELO ${Math.round(eloMatch.elo)}</span>` : '';
return `<td class="text-right font-mono font-bold">${fmtPts(r.ITCPoints)}` + return `<td class="text-right font-mono font-bold">${fmtPts(r.ITCPoints)}` +
mobilePct + `</td>` + mobilePct + mobileElo + `</td>` +
`<td class="text-right font-mono hidden sm:table-cell">` + `<td class="text-right font-mono hidden sm:table-cell">` +
`<span class="text-success">${r.wins || 0}</span><span class="text-error">${r.losses || 0}</span>${r.ties || 0}` + `<span class="text-success">${r.wins || 0}</span><span class="text-error">${r.losses || 0}</span>${r.ties || 0}` +
careerSub + `</td>` + careerSub + `</td>` +
@@ -333,11 +335,9 @@ function render(state) {
})() + })() +
`<td class="text-right font-mono text-base-content/40 hidden sm:table-cell">#${r.placing ?? "—"}</td>` + `<td class="text-right font-mono text-base-content/40 hidden sm:table-cell">#${r.placing ?? "—"}</td>` +
(() => { (() => {
const uid = r.userId || r.user?.id; if (eloMatch === undefined) return `<td class="text-center font-mono hidden sm:table-cell"><span class="opacity-20">…</span></td>`;
const e = findElo(fullName(r.user || {}), uid); if (!eloMatch) return `<td class="text-center font-mono text-base-content/40 hidden sm:table-cell">—</td>`;
if (e === undefined) return `<td class="text-center font-mono"><span class="opacity-20">…</span></td>`; return `<td class="text-center font-mono hidden sm:table-cell">${Math.round(eloMatch.elo)}</td>`;
if (!e) return `<td class="text-center font-mono text-base-content/40">—</td>`;
return `<td class="text-center font-mono">${Math.round(e.elo)}</td>`;
})(); })();
tb.appendChild(tr); tb.appendChild(tr);
}); });
@@ -406,7 +406,7 @@ function showSkeleton() {
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-14 ml-auto"></div></td>` + `<td class="hidden sm:table-cell"><div class="skeleton h-4 w-14 ml-auto"></div></td>` +
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-12 ml-auto"></div></td>` + `<td class="hidden sm:table-cell"><div class="skeleton h-4 w-12 ml-auto"></div></td>` +
`<td class="hidden sm:table-cell"><div class="skeleton h-4 w-10 ml-auto"></div></td>` + `<td class="hidden sm:table-cell"><div class="skeleton h-4 w-10 ml-auto"></div></td>` +
`<td><div class="skeleton h-4 w-12 mx-auto"></div></td>`; `<td class="hidden sm:table-cell"><div class="skeleton h-4 w-12 mx-auto"></div></td>`;
tb.appendChild(tr); tb.appendChild(tr);
} }
} }