Files
myice/myice/myice.py
T

300 lines
8.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Tool to work with My Ice Hockey schedules
"""
import configparser
import datetime
import json
import os
import re
import sys
from enum import Enum
from pathlib import Path
from typing import Annotated
import requests
import typer
user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0"
app = typer.Typer(no_args_is_help=True)
session: requests.Session
userid: int
class AgeGroup(str, Enum):
u13e = "U13 (Elite)"
u13t = "U13 (Top)"
u13a = "U13 (A)"
u13p = "U13 (Prép)"
u15t = "U15 (Top)"
u15a = "U15 (A)"
class EventType(str, Enum):
game = "game"
practice = "practice"
def load_cookies(file: str = "cookies.txt") -> requests.cookies.RequestsCookieJar:
cookie_jar_file = Path(file)
cj_dict = {}
if cookie_jar_file.exists():
with cookie_jar_file.open("rb") as f:
cj_dict = json.load(f)
return requests.cookies.cookiejar_from_dict(cj_dict)
def save_cookies():
with open("cookies.txt", "w") as f:
f.write(json.dumps(requests.utils.dict_from_cookiejar(session.cookies)))
def get_login(local_file: str = "myice.ini") -> tuple[str, str, int]:
config = configparser.ConfigParser()
config.read(
[
Path("~/.config/myice.ini").expanduser(),
"myice.ini",
local_file,
]
)
if "default" in config.sections():
default_config = config["default"]
username = default_config.get("username")
password = default_config.get("password")
userid = default_config.getint("userid")
if not username or not password:
print("Error: please configure username/password in ini file")
sys.exit(1)
else:
print("Error: please configure username/password in ini file")
sys.exit(1)
return username, password, userid
def do_login():
global session
global userid
username, password, userid = get_login()
r = session.get("https://app.myice.hockey/", headers={"User-Agent": user_agent})
r.raise_for_status()
form_data = {
"login_email": username,
"login_password": password,
"sublogin": 1,
"login_submit": "",
}
r = session.post(
"https://app.myice.hockey/classes/process.php",
data=form_data,
headers={
"Referer": "https://app.myice.hockey/",
"Origin": "https://app.myice.hockey",
"User-Agent": user_agent,
},
)
r.raise_for_status()
def get_userid():
global session
r = session.get(
"https://app.myice.hockey/players/clubschedule/",
headers={
"User-Agent": user_agent,
"Referer": "https://app.myice.hockey/classes/process.php",
},
)
r.raise_for_status()
for line in r.text.splitlines():
m = re.search(r"^\s+userid: '(?P<userid>[0-9]+)'", line)
if m:
userid = m.group("userid")
break
return userid
def wrapper_session(func):
def wrapper(*args, **kwargs):
global session, userid
session = requests.Session()
session.cookies = load_cookies()
session.cookies.clear_expired_cookies()
if not session.cookies.get("mih_v3_cookname"):
print("login...")
do_login()
save_cookies()
_, _, userid = get_login()
if not userid:
print("get userid...")
userid = get_userid()
return func(*args, **kwargs)
return wrapper
@wrapper_session
def get_schedule() -> str:
global session
global userid
assert session and userid
now = datetime.datetime.now()
date_start = now + datetime.timedelta(days=1)
date_end = date_start + datetime.timedelta(days=7)
r = session.post(
"https://app.myice.hockey/inc/processclubplanning.php",
data={
"type": "fetchmy",
"userid": userid,
"type_location": ["*"],
"start": date_start.strftime("%Y-%m-%d"),
"end": date_end.strftime("%Y-%m-%d"),
},
headers={
"User-Agent": user_agent,
"Referer": "https://app.myice.hockey/players/clubschedule/",
},
)
r.raise_for_status()
return r.text
@wrapper_session
def game_pdf(gameid: int, outfile: Path):
global session, userid
assert session and userid
r = session.get(
f"https://app.myice.hockey/pdfexports/playerplatgamepdfgenerator.php?id={gameid}",
headers={
"User-Agent": user_agent,
"Referer": "https://app.myice.hockey/inc/processclubplanning.php",
},
)
r.raise_for_status()
with outfile.open("wb") as fd:
for chunk in r.iter_content(chunk_size=1024 * 1024 * 4):
fd.write(chunk)
@wrapper_session
def practice_pdf(gameid: int, outfile: Path):
global session, userid
assert session and userid
r = session.get(
f"https://app.myice.hockey/pdfexports/playerplatpracticepdf.php?practice={gameid}",
headers={
"User-Agent": user_agent,
"Referer": "https://app.myice.hockey/inc/processclubplanning.php",
},
)
r.raise_for_status()
with outfile.open("wb") as fd:
for chunk in r.iter_content(chunk_size=1024 * 1024 * 4):
fd.write(chunk)
@app.command()
def schedule(
outfile: Annotated[
Path | None,
typer.Option(
"--outfile", "-o", help="file to write result to, or stdout if none"
),
] = None,
):
"""
Fetch schedule as json
"""
schedule = get_schedule()
if outfile:
with outfile.open("w") as f:
f.write(schedule)
else:
print("Schedule:", file=sys.stderr)
print(schedule)
def os_open(file: str) -> None:
if os.uname().sysname == "Linux":
os.system(f"xdg-open {file}")
else:
print(f"Opening file {file}", file=sys.stderr)
os.system(f"open {file}")
@app.command("game")
def get_game_pdf(
game_id: Annotated[int, typer.Argument(help="ID of game to gen pdf for")],
):
"""
Genate the pdf for the game invitation
"""
output_filename = f"game_{game_id}.pdf"
game_pdf(game_id, Path(output_filename))
os_open(output_filename)
@app.command("practice")
def get_practice_pdf(
game_id: Annotated[int, typer.Argument(help="ID of practice to gen pdf for")],
):
"""
Genate the pdf for the practice invitation
"""
output_filename = f"practice_{game_id}.pdf"
practice_pdf(game_id, Path(output_filename))
os_open(output_filename)
@app.command("search")
def parse_schedule(
age_group: Annotated[AgeGroup | None, typer.Option(...)] = None,
event_type_filter: Annotated[
EventType | None,
typer.Option("--type", help="Only display events of this type"),
] = None,
schedule_file: Annotated[
Path, typer.Option(help="schedule json file to parse")
] = Path("schedule.json"),
):
"""
Parse schedule.json to look for specific games or practices
"""
with schedule_file.open("r") as f:
data = json.load(f)
# age_group filter
if age_group:
events = [x for x in data if x["agegroup"] == age_group]
else:
events = [x for x in data if x["agegroup"] in AgeGroup]
# event_type filter
if event_type_filter:
if event_type_filter.value == EventType.game:
events = [x for x in events if "event" in x and x["event"] == "Jeu"]
else:
events = [x for x in events if "event" not in x or x["event"] == "Jeu"]
for event in events:
if age_group:
raw_title = event["title"].removeprefix(age_group + "\n")
else:
raw_title = event["title"]
title = " ".join(raw_title.split("\n"))
start = datetime.datetime.fromisoformat(event["start"])
start_fmt = start.strftime("%H:%M")
end = datetime.datetime.fromisoformat(event["end"])
end_fmt = end.strftime("%H:%M")
if "event" in event and event["event"] == "Jeu":
event_type = "game"
else:
event_type = "practice"
print(
f"[{event['id_event']}] {event['date']} {event_type} {start_fmt}-> {end_fmt}"
)
print(f"{event_type}: {title}\n")
if __name__ == "__main__":
app()