From 9ad563ed3fe3dc2b53bce6dd0d8e2ee9f3da13bd Mon Sep 17 00:00:00 2001 From: josh Date: Sun, 29 Mar 2026 13:19:59 -0400 Subject: [PATCH] feat: add shots on goal bar to live game cards, clean up gitignore Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 2 + app/static/script.js | 25 +++++++- app/static/styles.css | 55 ++++++++++++++++++ tests/__pycache__/__init__.cpython-313.pyc | Bin 146 -> 0 bytes .../conftest.cpython-313-pytest-8.3.4.pyc | Bin 3193 -> 0 bytes ..._process_data.cpython-313-pytest-8.3.4.pyc | Bin 21912 -> 0 bytes .../test_routes.cpython-313-pytest-8.3.4.pyc | Bin 9691 -> 0 bytes 7 files changed, 81 insertions(+), 1 deletion(-) delete mode 100644 tests/__pycache__/__init__.cpython-313.pyc delete mode 100644 tests/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc delete mode 100644 tests/__pycache__/test_process_data.cpython-313-pytest-8.3.4.pyc delete mode 100644 tests/__pycache__/test_routes.cpython-313-pytest-8.3.4.pyc diff --git a/.gitignore b/.gitignore index e0313ee..8d8fc97 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ nhl_standings.db **/__pycache__ .venv/ +.coverage +.pytest_cache/ diff --git a/app/static/script.js b/app/static/script.js index 19e87c5..1613a4b 100644 --- a/app/static/script.js +++ b/app/static/script.js @@ -40,6 +40,8 @@ function renderLiveGame(game) { const dot = running ? `` : ''; + const shots = !intermission ? shotsBar(game['Away Shots'], game['Home Shots']) : ''; + const hype = !intermission ? `
Hype Meter @@ -59,6 +61,7 @@ function renderLiveGame(game) {
${teamRow(game, 'Away', 'live')} ${teamRow(game, 'Home', 'live')} + ${shots} ${hype} `; } @@ -101,7 +104,7 @@ function teamRow(game, side, state) { const pp = game[`${side} Power Play`]; const record = game[`${side} Record`]; - const sogHtml = (state !== 'pre' && sog !== undefined) + const sogHtml = (state === 'final' && sog !== undefined) ? `${sog} SOG` : ''; const ppHtml = pp ? `${pp}` : ''; @@ -120,6 +123,26 @@ function teamRow(game, side, state) { `; } +// ── Shots Bar ──────────────────────────────────────── + +function shotsBar(away, home) { + const total = (away || 0) + (home || 0); + const awayPct = total > 0 ? (away / total) * 100 : 50; + const homePct = total > 0 ? (home / total) * 100 : 50; + return ` +
+
+ ${away} +
+
+
+
+ ${home} +
+ Shots on Goal +
`; +} + // ── Gauge ──────────────────────────────────────────── function updateGauges() { diff --git a/app/static/styles.css b/app/static/styles.css index 101c35d..d114072 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -196,6 +196,61 @@ main { white-space: nowrap; } +/* ── Shots Bar ──────────────────────────────────── */ + +.shots-bar { + margin-top: 0.75rem; +} + +.shots-row { + display: flex; + align-items: center; + gap: 0.4rem; +} + +.shots-track { + flex: 1; + height: 5px; + border-radius: 99px; + overflow: hidden; + display: flex; + background: var(--badge-bg); +} + +.shots-fill { + height: 100%; + transition: width 0.5s ease; +} + +.shots-fill-away { + background: #4a90e2; + border-radius: 99px 0 0 99px; +} + +.shots-fill-home { + background: #f97316; + border-radius: 0 99px 99px 0; +} + +.shots-num { + font-size: 0.72rem; + font-weight: 700; + color: var(--text); + min-width: 1.25rem; + text-align: center; +} + +.shots-label { + display: block; + font-size: 0.6rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); + margin-top: 0.25rem; + text-align: center; +} + /* ── Hype Meter ─────────────────────────────────── */ .hype-meter { diff --git a/tests/__pycache__/__init__.cpython-313.pyc b/tests/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 1f5d8db15c9039985e65bf1480a8ec4dbfcb1889..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 zcmey&%ge<81P4@4W`gL)AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekklqSU1P#G(}alGNgoV*U8|%)HE!_;|g7%3B;Zx%nxjIjMF< VtUwb$HWY&xADI~$8H<>KECB9nA-@0s diff --git a/tests/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc deleted file mode 100644 index 6eb554d5c197632ffd6ef4cefef89611eb5b85aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3193 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 OpXv5Q+diPYXY@bbsT86B diff --git a/tests/__pycache__/test_process_data.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_process_data.cpython-313-pytest-8.3.4.pyc deleted file mode 100644 index fda81da6053f93b38376b99ecc17017d5e7e2dec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21912 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 A=>Px# diff --git a/tests/__pycache__/test_routes.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_routes.cpython-313-pytest-8.3.4.pyc deleted file mode 100644 index 3947e898ec71aac5a380cabcbb61a3f8fd921899..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9691 zcmeGi+in}jb(iFl!;3}R^euKQ5t1y6PAp2YWV?wKRFNIuWq>ONZq#D27FQAzYL}T^ z)wWWjDSCm@m-@wk`qHuwMXO)v7qoArq=3SrMGExEZ>fzSfS-EK?CvZ{Q?ioQE{bZf zIy>jgnahr5&XrI1?1>3bzUcc)F&Gwve<0$OkXv~=36)2JEXd+@fr{)MxE3@6Y2jLk zN@=GhOv6WnZb1$l73AP(!0AgPj1~f##A#$s%!D5!!{m&^=0MR!o~EJlNccXRiw0zo z260pxl7j#xIYh&AQYLJ7OlhWdRWE4wa>k;iJ;wFUxD&IXA^?N%_jCh>ekA0@oHqq) zc7QC7x>P-o59AvAyIeXW^!9Bf`|fz1f!r4dmM-5wbU zjJmwCG)+9t9NT;kbKc#;s7rVo-i2dLvm}~%5$hK60q+aEwW)`idUb5y>y`*>^1q}# zchH;Tg`6ReQw|RVPs6_GVL2j`e0We~SY&#ix1NTv_Iot{4PO@V#`4}89KSJ94~~iD zsW~ANowUX5<3B^6Rq9I7{1~cY>X{pwtsmvG!E%qQi z<}{3EOq)>6EE~G1tp@EN@FEySF#4=RHBF6LY>acj3dD1(F*`V?S$4c(Ks)x`@_h?V zq@q%LpE9GCN($A=bjDaHt5mZS(5&29ES9XIZrb6J0UV}%L^0=$#ZrN>Dsv^{j#^T5 zbwRVEimA?OID*|V&X!O#^h=Z))J`e023=4sMY~rnsk&-G%PO%$rdFD@6SF1N{79K8 z6*b+Wcwp=}PVIcgP$M$Zh@5c{XXLYu$axbFY}zgMNHz}}VrD-y%=zpDY-M)x%8k?V zj6t*Dy=-f!W-U_PRE7r!?XIb&>pRfOAi<>O&jEZYY^J*E zsh(P@r!ujg>R%?C$;0(zPc7L~C8sJT1GVI-I&q&ha;lo_;iyyjoSa&|$7wYmg9gsM zj^lAAUfCe0p29-5kS=RGlrA7ehTF*(lr4DI|9#ogaG$u0!F4+Y?i0VoaO1h`$+eX# z+eo4I65>TtsQrp~uNEoPp8M6x7K#eQ#IJ=Dp7u}ps%Qs_dPZD&69vmq#!MG=CLchQ zIARAXuGl#PVyB>5YU}~Kiy0@R_gN!D)nwX1HyY}k0f?0=9IN@*=QtE^;1%Gf z8Cb=tFqw(iL}4;iQS6waEEt8wk_PpJqI|rlmQcC^mQ5 z2NeYcdvQilEsGZKpbP_s5oEwg*ltnOm<(XW#J&H8@FWnB-uQ|Hq+y2`_0;fxMI*Bt*8b@K2JYQX?EPtO?dR~O zwiL9i+u8M=Ez10Yi}_%S?*2X4-duaNxA3`NFyZ?J4Sn!G*iv9EzV4HzxdzUm4rwWE zJP3b1?d@!C7xcShvCHK5YQG}h3w1YG`+>IB{y}ef4PS`bt)*;j?UygfQDb+XVZUDb zKF4S!{sYpsiROt2JSqyiR&HlSiwh zd!kJ9hW^RqS& zjCEo$wxt*&yY#A>NUt#_Z8qCwp|3 zUWQpSiI%{vV^UAzs<>TEuOZrtpa(#snLXNwoMYifFGGWGC!p(?+`fmQ%&XvRQ0?Qz zA_aqcwy2c~+nL-i=!v0d^8?uJ9huzm3?nW$Ov|SxaQAsS^&m(NJGUZqCIk z!185MNOU@W2`2Hq!jt*}96b0{0wjd53)dheoW^h+io}+J9t8X%!5ss*#2k*%Qe!AkO#*<_|JBH%grOZ6pq)W17%;qH)7QE|O_smCmLD)_X=Of^wL~d&h64~$5K6DrM z=9S4b=8ss(wGFU6m(ugc#{5!Cf$!x0kM|YKNAY{LUlH#Gwlk5VuJk8zOpfP?EB&L> znAT#Y!L!1&R!f0Za82TUsq3T6f!t3_GHGugkSA{CF1>qeJaoc1$go2b?^%9d_RlQv+3CQ5IIA zS76EXDguns>^P6(oe&JiCU04lO)fae%XjDuut01} mFw+3O42q)og&_P)_-Xv>a6}ybrvTvV1D#^m69E8Q