feat: add Stat-Check ELO column with daily auto-refresh
- Scraper (statcheck-elo-scraper/refresh_elo.py) downloads the stat-check ELO workbook from OneDrive and builds public/elo-data.json - GitHub Actions workflow (.github/workflows/refresh-elo.yml) runs daily at 06:00 UTC and commits updated ELO data - Frontend fetches elo-data.json and matches players by name, using BCP career event history to disambiguate same-name candidates - ELO score shown in new sortable Stat-Check ELO column, visible on all screen sizes; unmatched players show — Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
import base64
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
from download_and_parse import download, xlsx_to_csv
|
||||
|
||||
|
||||
EMBED_URL = (
|
||||
"https://1drv.ms/x/c/9f33f29504402fa1/"
|
||||
"UQShL0AElfIzIICfIQ4AAAAAAGSZ8S-PJuKWQmM"
|
||||
"?em=2&ActiveCell='Sheet1'!A1&Item='Sheet1'!A1:L40318"
|
||||
"&wdHideGridlines=True&wdHideHeaders=True&wdDownloadButton=True"
|
||||
"&wdInConfigurator=True&wdInConfigurator=True"
|
||||
)
|
||||
|
||||
BADGER_APP_ID = "00000000-0000-0000-0000-0000481710a4"
|
||||
|
||||
|
||||
def request_json(url: str, *, method: str = "GET", data: bytes | None = None, headers: dict[str, str] | None = None) -> dict:
|
||||
req = Request(url, data=data, method=method, headers=headers or {})
|
||||
with urlopen(req, timeout=60) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def badger_token() -> str:
|
||||
payload = json.dumps({"appid": BADGER_APP_ID}, separators=(",", ":")).encode()
|
||||
data = request_json(
|
||||
"https://api-badgerp.svc.ms/v1.0/token",
|
||||
method="POST",
|
||||
data=payload,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Origin": "https://onedrive.live.com",
|
||||
"Referer": "https://onedrive.live.com/",
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
},
|
||||
)
|
||||
if data.get("authScheme") != "badger" or not data.get("token"):
|
||||
raise RuntimeError(f"Unexpected Badger token response: {data!r}")
|
||||
return data["token"]
|
||||
|
||||
|
||||
def share_id(url: str) -> str:
|
||||
encoded = base64.urlsafe_b64encode(url.encode()).decode().rstrip("=")
|
||||
return "u!" + encoded
|
||||
|
||||
|
||||
def download_url_for_embed(embed_url: str) -> str:
|
||||
token = badger_token()
|
||||
select = "id,openWith,officebundle,currentUserRole,eTag,name,size,content.downloadUrl,file,sharepointIds,sensitivityLabel,webUrl,webDavUrl,parentReference,vault"
|
||||
url = (
|
||||
"https://my.microsoftpersonalcontent.com/_api/v2.0/shares/"
|
||||
f"{share_id(embed_url)}/driveItem?action=EmbedView&$select={quote(select, safe=',')}"
|
||||
)
|
||||
data = request_json(
|
||||
url,
|
||||
method="POST",
|
||||
data=b"",
|
||||
headers={
|
||||
"Accept": "application/json",
|
||||
"Authorization": f"Badger {token}",
|
||||
"Origin": "https://onedrive.live.com",
|
||||
"Prefer": "autoredeem",
|
||||
"Referer": "https://onedrive.live.com/",
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
},
|
||||
)
|
||||
if "@content.downloadUrl" not in data:
|
||||
raise RuntimeError(f"No download URL in metadata response: {data!r}")
|
||||
return data["@content.downloadUrl"]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
xlsx_path = Path(sys.argv[1] if len(sys.argv) > 1 else "elo-pure.xlsx")
|
||||
csv_path = Path(sys.argv[2] if len(sys.argv) > 2 else "elo-pure.csv")
|
||||
url = download_url_for_embed(EMBED_URL)
|
||||
download(url, xlsx_path)
|
||||
xlsx_to_csv(xlsx_path, csv_path)
|
||||
print(f"downloaded={xlsx_path.stat().st_size} bytes csv={csv_path.stat().st_size} bytes")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user