feat: make config_section a global CLI option

Users can now specify --config-section once at the beginning of the command instead of repeating it for each subcommand. Also bumped version to v0.5.0 for this new feature.
This commit is contained in:
2025-08-19 09:25:50 +02:00
parent 2a5883375f
commit c4d9236b16
2 changed files with 54 additions and 75 deletions

View File

@@ -92,6 +92,23 @@ def sanitize_json_response(text):
app = typer.Typer(no_args_is_help=True)
session: requests.Session
userid: int
global_config_section: str = "default"
# Add global option for config section
@app.callback()
def main(
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
"""My Ice Hockey schedule tool"""
# Store the config_section in a global variable so it can be accessed by commands
global global_config_section
global_config_section = config_section
class AgeGroup(str, Enum):
@@ -210,12 +227,15 @@ def select_club(club_id: int = 172):
r.raise_for_status()
def do_login(config_section: str = "default"):
def do_login(config_section: str | None = None):
global session
global userid
username, password, userid_tmp, token, club_id = get_login(
config_section=config_section
)
global global_config_section
# Use provided config_section, or fall back to global one
section = config_section if config_section is not None else global_config_section
username, password, userid_tmp, token, club_id = get_login(config_section=section)
if userid_tmp is not None:
userid = userid_tmp
r = session.get("https://app.myice.hockey/", headers={"User-Agent": user_agent})
@@ -261,9 +281,9 @@ def get_userid():
def wrapper_session(func):
def wrapper(*args, **kwargs):
global session, userid
# Extract config_section from kwargs if present
config_section = kwargs.get("config_section", "default")
global session, userid, global_config_section
# Use the global config_section
config_section = global_config_section
session = requests.Session()
# session.verify = False
@@ -271,7 +291,7 @@ def wrapper_session(func):
session.cookies.clear_expired_cookies()
if not session.cookies.get("mih_v3_cookname"):
print("login...", file=sys.stderr)
do_login(config_section=config_section)
do_login(config_section=None) # Use global config_section
save_cookies()
_, _, userid, _, _ = get_login(config_section=config_section)
if not userid:
@@ -284,7 +304,7 @@ def wrapper_session(func):
@wrapper_session
def get_schedule(num_days: int, config_section: str = "default") -> str:
def get_schedule(num_days: int) -> str:
global session
global userid
assert session and userid
@@ -315,7 +335,7 @@ def get_schedule(num_days: int, config_section: str = "default") -> str:
@wrapper_session
def game_pdf(gameid: int, outfile: Path, config_section: str = "default"):
def game_pdf(gameid: int, outfile: Path):
global session, userid
assert session and userid
r = session.get(
@@ -332,7 +352,7 @@ def game_pdf(gameid: int, outfile: Path, config_section: str = "default"):
@wrapper_session
def practice_pdf(gameid: int, outfile: Path, config_section: str = "default"):
def practice_pdf(gameid: int, outfile: Path):
global session, userid
assert session and userid
r = session.get(
@@ -357,17 +377,12 @@ def schedule(
),
] = None,
num_days: Annotated[int, typer.Option("--days")] = 7,
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
"""
Fetch schedule as json
"""
schedule = get_schedule(num_days, config_section=config_section)
global global_config_section
schedule = get_schedule(num_days)
# Sanitize the JSON response using our proven approach
sanitized_schedule = sanitize_json_response(schedule)
if outfile:
@@ -406,21 +421,16 @@ def extract_players(pdf_file: Path) -> List[str]:
def get_game_pdf(
game_id: Annotated[int, typer.Argument(help="ID of game to gen pdf for")],
open_file: Annotated[bool, typer.Option("--open", "-o")] = False,
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
"""
Genate the pdf for the game invitation
"""
global global_config_section
if open_file:
output_filename = f"game_{game_id}.pdf"
else:
output_filename = tempfile.NamedTemporaryFile().name
game_pdf(game_id, Path(output_filename), config_section=config_section)
game_pdf(game_id, Path(output_filename))
if open_file:
os_open(output_filename)
else:
@@ -432,18 +442,13 @@ def get_game_pdf(
@app.command("practice")
def get_practice_pdf(
game_id: Annotated[int, typer.Argument(help="ID of practice to gen pdf for")],
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
"""
Genate the pdf for the practice invitation
"""
global global_config_section
output_filename = f"practice_{game_id}.pdf"
practice_pdf(game_id, Path(output_filename), config_section=config_section)
practice_pdf(game_id, Path(output_filename))
os_open(output_filename)
@@ -457,16 +462,11 @@ def parse_schedule(
schedule_file: Annotated[
Path, typer.Option(help="schedule json file to parse")
] = Path("schedule.json"),
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
"""
Parse schedule.json to look for specific games or practices
"""
global global_config_section
try:
with schedule_file.open("r") as f:
data = json.load(f)
@@ -512,16 +512,11 @@ def check_with_ai(
schedule_file: Annotated[
Path, typer.Option(help="schedule json file to parse")
] = Path("schedule.json"),
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
"""
Search through the schedule with natural language using Infomaniak LLM API
"""
global global_config_section
if not utils.init():
sys.exit(1)
with schedule_file.open("r") as f:
@@ -570,10 +565,14 @@ mobile_headers = {
}
def mobile_login(config_section: str = "default"):
def mobile_login(config_section: str | None = None):
global global_config_section
import base64
username, password, _, token, club_id = get_login(config_section=config_section)
# Use provided config_section, or fall back to global one
section = config_section if config_section is not None else global_config_section
username, password, _, token, club_id = get_login(config_section=section)
if token and club_id:
return {"id": 0, "token": token, "id_club": club_id}
@@ -612,30 +611,16 @@ def refresh_data():
@app.command("mobile-login")
def do_mobile_login(
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
global userdata
userdata = mobile_login(config_section=config_section)
def do_mobile_login():
global userdata, global_config_section
userdata = mobile_login(config_section=global_config_section)
print(json.dumps(userdata, indent=2))
@app.command("mobile")
def mobile(
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
global userdata
userdata = mobile_login(config_section=config_section)
def mobile():
global userdata, global_config_section
userdata = mobile_login(config_section=global_config_section)
games = [x for x in refresh_data().get("club_games")]
print(json.dumps(games, indent=2))
@@ -644,15 +629,9 @@ def mobile(
def mobile_game(
game_id: Annotated[int, typer.Argument(help="game id")],
raw: Annotated[bool, typer.Option(help="display raw output")] = False,
config_section: Annotated[
str,
typer.Option(
"--config-section", "-c", help="Configuration section to use from INI file"
),
] = "default",
):
global userdata
userdata = mobile_login(config_section=config_section)
global userdata, global_config_section
userdata = mobile_login(config_section=global_config_section)
# data = refresh_data()
with requests.post(

View File

@@ -1,6 +1,6 @@
[project]
name = "myice"
version = "v0.4.3"
version = "v0.5.0"
description = "myice parsing"
authors = [
{ name = "Rene Luria", "email" = "<rene@luria.ch>"},