initial import

This commit is contained in:
2024-11-01 09:59:45 +01:00
commit e102cfa9c8
7 changed files with 1414 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
myice.ini
*.pdf
schedule.json
.envrc
cookies.txt

63
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,63 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: check-case-conflict
- id: end-of-file-fixer
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: check-yaml
- id: check-added-large-files
- id: detect-private-key
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.6.8
hooks:
# Run the linter.
- id: ruff
args: [--fix]
# Run the formatter.
- id: ruff-format
args: [--diff, --target-version, py312]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
exclude: ^(docs/|example-plugin/)
args: [--ignore-missing-imports]
additional_dependencies: [types-requests]
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.35.1
hooks:
- id: yamllint
args: [--strict]
- repo: https://github.com/markdownlint/markdownlint
rev: v0.12.0
hooks:
- id: markdownlint
exclude: "^.github|(^docs/_sidebar\\.md$)"
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.10.0.1
hooks:
- id: shellcheck
args: ["--severity=error"]
exclude: "^.git"
files: "\\.sh$"
- repo: https://github.com/golangci/misspell
rev: v0.6.0
hooks:
- id: misspell
args: ["-i", "charactor"]
- repo: https://github.com/python-poetry/poetry
rev: "1.8.0"
hooks:
- id: poetry-check
- id: poetry-lock
- id: poetry-install

59
README.md Normal file
View File

@@ -0,0 +1,59 @@
# myice
## récupérer le schedule
```shell
./myice.py schedule -o schedule.json
```
## data
Pour récupérer les event des U13 Elite par exemple:
```shell
jq -r '.[] | select(.agegroup == "U13 (Elite)") | [.date, .id_event, .event, .title] | join(" - ")' schedule.json
2024-11-01 - 561896 - - U13 (Elite)
Spécial
Shooting Box Groupe #3
2024-11-01 - 561904 - - U13 (Elite)
Off-Ice
Patinoire des Vernets - Patinoire Extérieure
2024-11-01 - 561855 - - U13 (Elite)
On-Ice
Patinoire des Vernets - Patinoire Extérieure
2024-11-04 - 576653 - - U13 (Elite)
Off-Ice
Patinoire des Vernets - Patinoire Extérieure
2024-11-04 - 572066 - - U13 (Elite)
On-Ice
Patinoire des Vernets - Patinoire Extérieure
2024-11-05 - 576652 - - U13 (Elite)
Off-Ice
Patinoire des Vernets - Patinoire Extérieure
2024-11-05 - 572068 - - U13 (Elite)
On-Ice
Patinoire des Vernets - Patinoire Extérieure
2024-11-02 - 117015 - Jeu - U13 (Elite)
Saison
HC Ajoie
```
### match
alors pour avoir la convocation du match contre Ajoie, l'id c'est 117015:
```shell
./get_schedule.py game 117015
Opening file game_117015.pdf
```
### entraînement
et pour la convoc d'un entraînement:
```shell
./myice.py practice 561855
Opening file practice_561855.pdf
```

0
myice/__init__.py Normal file
View File

237
myice/myice.py Executable file
View File

@@ -0,0 +1,237 @@
#!/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 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
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 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))
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))
open(output_filename)
if __name__ == "__main__":
app()

1028
poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

22
pyproject.toml Normal file
View File

@@ -0,0 +1,22 @@
[tool.poetry]
name = "myice"
version = "0.1.0"
description = ""
authors = ["Rene Luria <rene.luria@infomaniak.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
requests = "^2.32.3"
typer = {extras = ["all"], version = "^0.12.5"}
[tool.poetry.group.dev.dependencies]
ipykernel = "^6.29.5"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
myice = 'myice.myice:app'