From cb798a70ce3b1a8068a13c549ad2e57aa040b80e Mon Sep 17 00:00:00 2001
From: Marc Koch <marc-koch@posteo.de>
Date: Mon, 16 Sep 2024 12:33:36 +0200
Subject: [PATCH] add argument parsing

---
 src/cgnappointments/__main__.py | 114 +++++++++++++++++++++++++++-----
 1 file changed, 98 insertions(+), 16 deletions(-)

diff --git a/src/cgnappointments/__main__.py b/src/cgnappointments/__main__.py
index eacf797..7ebb26e 100644
--- a/src/cgnappointments/__main__.py
+++ b/src/cgnappointments/__main__.py
@@ -1,3 +1,4 @@
+import argparse
 import csv
 import json
 import logging
@@ -22,18 +23,96 @@ PROJECT_ROOT = Path(__file__).parent.parent
 
 logger = logging.getLogger(__package__)
 
+def parse_arguments() -> dict:
+    """
+    Parse the arguments.
+    :return: dict
+    """
+    argparser = argparse.ArgumentParser(
+        prog="cgn-appointments",
+        description="Scrapes appointments from termine.stadt-koeln.de an sends a message to a ntfy server.",
+    )
+    argparser.add_argument(
+        "-s",
+        "--services",
+        action="store",
+        nargs='+',
+        type=str,
+        help="Services to check",
+        required=False,
+    )
+    argparser.add_argument(
+        "-l",
+        "--locations",
+        action="store",
+        nargs='+',
+        type=str,
+        help="Locations to check",
+        required=False,
+    )
+    argparser.add_argument(
+        "--config-file",
+        action="store",
+        type=Path,
+        help="Path to the configuration file",
+        required=False,
+    )
+    argparser.add_argument(
+        "--log-file",
+        action="store",
+        type=Path,
+        help="Path to logfile",
+        required=False,
+    )
+    argparser.add_argument(
+        "--log-level",
+        action="store",
+        type=str,
+        choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"],
+        help="Logging Level",
+        required=False,
+    )
+    return argparser.parse_args().__dict__
+
+
+def update_config_with_args(config: dict, args: dict) -> dict:
+    """
+    Update the configuration with the arguments.
+    :param config:
+    :param args:
+    :return: dict
+    """
+    update_config = {
+        "services": args.get("services"),
+        "locations": args.get("locations"),
+        "csv_path": args.get("csv_path"),
+    }
+    for key, value in update_config.items():
+        if value is not None:
+            config[key] = value
+    if args.get("log_file") is not None:
+        config["logging"]["handlers"]["file"]["filename"] = args.get("log_file")
+    if args.get("log_level") is not None:
+        config["logging"]["loggers"]["root"]["level"] = args.get("log_level")
+    return config
+
 
 def get_config() -> dict:
     """
     Get the configuration from the config.yaml file.
-    :return:
+    :return: dict
     """
-    config_yaml = Path(user_config_dir()) / "cgn-appointments" / "config.yaml"
+    args = parse_arguments()
+
+    if args.get("config_file") is not None:
+        config_yaml = args.get("config_file")
+    else:
+        config_yaml = Path(user_config_dir()) / "cgn-appointments" / "config.yaml"
 
     if not config_yaml.exists():
-        logger.info(f"""config.yaml not found.
-                    Creating a new one under '{config_yaml}'.
-                    Please fill in the required information.""")
+        print(f"""config.yaml not found.
+              Creating a new one under '{config_yaml}'.
+              Please fill in the required information.""")
         config_yaml.parent.mkdir(parents=True, exist_ok=True)
         config_yaml.touch()
         config_yaml.write_text(Path(Path(__file__).parent, "config_template.yaml").read_text())
@@ -41,18 +120,21 @@ def get_config() -> dict:
 
     try:
         with open(config_yaml, "r") as file:
-            return dict(yaml.safe_load(file))
+            config = dict(yaml.safe_load(file))
     except FileNotFoundError:
-        print("config.yaml not found")
+        print(f"config.yaml not found in '{config_yaml}'.")
         exit(1)
 
+    # Replace config values with given arguments
+    return update_config_with_args(config, args)
+
 
 def define_csv_path(csv_path: str|None, csv_name: str|None) -> Path:
     """
     Define the path to the csv file.
     :param csv_path:
     :param csv_name:
-    :return:
+    :return: Path
     """
     csv_path = Path(csv_path) if csv_path else None
     csv_name = Path(csv_name) if csv_name else None
@@ -233,16 +315,16 @@ def main():
 
     # Get location containers
     location_containers = driver.find_elements(By.CLASS_NAME, "location-container")
-    for location_container in location_containers:
-        loc_title = location_container.find_element(By.CLASS_NAME, "location_title")
-        for loc in check_locations:
+    for loc in check_locations:
+        for location_container in location_containers:
+            loc_title = location_container.find_element(By.CLASS_NAME, "location_title")
             if loc in loc_title.text:
                 locations.update({loc: {"location_container": location_container}})
-        if len(locations) > 0:
-            logger.debug(f"Location containers found",
-                 extra={"locations": locations})
-        else:
-            logger.warning("No location containers found.")
+    if len(locations) > 0:
+        logger.debug(f"Location containers found",
+             extra={"locations": locations})
+    else:
+        logger.warning("No location containers found.")
 
     # Get earliest date for each location
     for loc in locations.keys():