diff --git a/pygeoweaver/__main__.py b/pygeoweaver/__main__.py index 0ec9367..11e46fc 100644 --- a/pygeoweaver/__main__.py +++ b/pygeoweaver/__main__.py @@ -54,7 +54,7 @@ def start_command(force_download, force_restart): """ Start the Geoweaver application. """ - start(force_download=force_download, force_restart=force_restart) + start(force_download=force_download, force_restart=force_restart, exit_on_finish=True) @geoweaver.command("stop") @@ -62,7 +62,7 @@ def stop_command(): """ Start the Geoweaver application. """ - stop() + stop(exit_on_finish=True) @geoweaver.command("show") @click.option('--geoweaver-url', default=GEOWEAVER_DEFAULT_ENDPOINT_URL, help='Geoweaver URL (default is GEOWEAVER_DEFAULT_ENDPOINT_URL)') diff --git a/pygeoweaver/commands/pgw_history.py b/pygeoweaver/commands/pgw_history.py index 61449d4..a5af446 100644 --- a/pygeoweaver/commands/pgw_history.py +++ b/pygeoweaver/commands/pgw_history.py @@ -1,28 +1,99 @@ +import http +import json +import logging import subprocess +import traceback +from urllib.parse import urlencode, urlparse +import click import pandas as pd import requests +from tabulate import tabulate from pygeoweaver.constants import * +from pygeoweaver.server import check_geoweaver_status, start from pygeoweaver.utils import ( download_geoweaver_jar, get_geoweaver_jar_path, get_java_bin_path, get_root_dir, + get_spinner, + is_interactive, ) +logger = logging.getLogger(__name__) + + +def display_response_table(response_json): + """ + Convert the response JSON into a table format and display it. + + Args: + response_json (str or dict): The JSON response to display. + """ + if isinstance(response_json, str): + data = json.loads(response_json) + else: + data = response_json + + # Convert data into a DataFrame for better visualization + df = pd.DataFrame([data]) + + # Check if running in Jupyter + if is_interactive(): + from IPython.display import display + display(df) + else: + # Use tabulate for terminal display + table = tabulate(df, headers='keys', tablefmt='psql') + print(table) + + def show_history(history_id): """ - Workflow and process history uses the same method to check + History uses the same method to check """ if not history_id: raise RuntimeError("history id is missing") - download_geoweaver_jar() - subprocess.run( - [get_java_bin_path(), "-jar", get_geoweaver_jar_path(), "history", history_id], - cwd=f"{get_root_dir()}/", - ) + + try: + if not check_geoweaver_status(): + start() + + log_url = f"{GEOWEAVER_DEFAULT_ENDPOINT_URL}/web/log" + json_data = f"type=process&id={history_id}" + headers = { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + } + response = requests.post(log_url, headers=headers, data=json_data) + + if response.status_code == 200: + try: + display_response_table(response.json()) + except requests.exceptions.JSONDecodeError: + raise ValueError('Response is not in JSON format:', response.text) + else: + raise ValueError(f'POST request failed with status code {response.text}') + except Exception as e: + with get_spinner(text=f'Get workflow history...', spinner='dots'): + logger.error(e) + traceback_str = traceback.format_exc() + print(f"Error occurred: {str(e)}") + print(f"Traceback:\n{traceback_str}") + + download_geoweaver_jar() + process = subprocess.run( + [get_java_bin_path(), "-jar", get_geoweaver_jar_path(), "history", history_id], + cwd=f"{get_root_dir()}/", + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) + + print(process.stdout) + if process.stderr: + print("=== Error ===") + print(process.stderr) + logger.error(process.stderr) def get_process_history(process_id): @@ -32,22 +103,43 @@ def get_process_history(process_id): """ if not process_id: raise Exception("please pass `process_id` as a parameter to the function.") - download_geoweaver_jar() try: + print("why it is still here") + if check_geoweaver_status(): + start(exit_on_finish=False) + print("why it is still here") + + json_data = f"type=process&id={process_id}" + print(json_data) + headers = { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + } r = requests.post( f"{GEOWEAVER_DEFAULT_ENDPOINT_URL}/web/logs", - data={"type": "process", "id": process_id}, - ).json() - df = pd.DataFrame(r) - df["history_begin_time"] = pd.to_datetime(df["history_begin_time"], unit="ms") - df["history_end_time"] = pd.to_datetime(df["history_end_time"], unit="ms") - return df - except Exception as e: - subprocess.run( - f"{get_java_bin_path()} -jar {get_geoweaver_jar_path()} process-history {process_id}", - cwd=f"{get_root_dir()}/", - shell=True, + data=json_data, + headers=headers, ) + print("r.text = ", r.text) + if r.status_code == 200: + try: + display_response_table(r.json()) + except requests.exceptions.JSONDecodeError: + raise ValueError('Response is not in JSON format:', r.text) + else: + raise ValueError(f'POST request failed with status code {r.text}') + except Exception as e: + with get_spinner(text=f'Get workflow history...', spinner='dots'): + process = subprocess.run( + f"{get_java_bin_path()} -jar {get_geoweaver_jar_path()} process-history {process_id}", + cwd=f"{get_root_dir()}/", + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) + + print(process.stdout) + if process.stderr: + print("=== Error ===") + print(process.stderr) + logger.error(process.stderr) def get_workflow_history(workflow_id): @@ -58,6 +150,8 @@ def get_workflow_history(workflow_id): if not workflow_id: raise Exception("please pass `workflow_id` as a parameter to the function.") try: + if check_geoweaver_status(): + start() r = requests.post( f"{GEOWEAVER_DEFAULT_ENDPOINT_URL}/web/logs", data={"type": "workflow", "id": workflow_id}, @@ -65,13 +159,23 @@ def get_workflow_history(workflow_id): df = pd.DataFrame(r) df["history_begin_time"] = pd.to_datetime(df["history_begin_time"], unit="ms") df["history_end_time"] = pd.to_datetime(df["history_end_time"], unit="ms") - return df + if is_interactive(): + return df + else: + print(df) except Exception as e: - subprocess.run( - f"{get_java_bin_path()} -jar {get_geoweaver_jar_path()} workflow-history {workflow_id}", - shell=True, - cwd=f"{get_root_dir()}/", - ) + with get_spinner(text=f'Get workflow history via slow CLI...', spinner='dots'): + process = subprocess.run( + f"{get_java_bin_path()} -jar {get_geoweaver_jar_path()} workflow-history {workflow_id}", + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, + cwd=f"{get_root_dir()}/", + ) + + print(process.stdout) + if process.stderr: + print("=== Error ===") + print(process.stderr) + logger.error(process.stderr) def save_history(code: str = None, status: str = None, log_output: str = None, ): diff --git a/pygeoweaver/log_config.py b/pygeoweaver/log_config.py index 2c29b3d..e48618f 100644 --- a/pygeoweaver/log_config.py +++ b/pygeoweaver/log_config.py @@ -9,7 +9,11 @@ def setup_logging(): # Ensure the directory for the log file exists, create if not log_dir = os.path.dirname(log_file) os.makedirs(log_dir, exist_ok=True) - with open('logging.ini', 'rt') as f: + # Get the absolute path to the logging.ini file + logging_ini_path = os.path.abspath('logging.ini') + + # Open the logging.ini file + with open(logging_ini_path, 'rt') as f: config_str = f.read() config_str = config_str.replace('%(log_file)s', os.path.expanduser(log_file)) diff --git a/pygeoweaver/server.py b/pygeoweaver/server.py index 78bcff0..f83cfbd 100644 --- a/pygeoweaver/server.py +++ b/pygeoweaver/server.py @@ -61,7 +61,7 @@ def check_geoweaver_status() -> bool: raise ValueError(err_msg) -def start_on_windows(): +def start_on_windows(force_restart=False, force_download=False, exit_on_finish=True): with get_spinner(text=f'Stop running Geoweaver if any...', spinner='dots'): subprocess.run(["taskkill", "/f", "/im", "geoweaver.exe"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) @@ -103,13 +103,15 @@ def start_on_windows(): with open(log_file, "r") as f: print(f.read()) print("Success: Geoweaver is up") - safe_exit(0) + if exit_on_finish: + safe_exit(0) except Exception as e: # print(f"Error occurred during request: {e}") continue print("Error: Geoweaver is not up") - safe_exit(1) + if exit_on_finish: + safe_exit(1) def stop_on_windows(): @@ -137,18 +139,17 @@ def check_java_exists(): return None -def start_on_mac_linux(force_restart=True): - +def start_on_mac_linux(force_restart: bool=False, force_download: bool=False, exit_on_finish: bool=False): if force_restart: # First stop any existing Geoweaver - stop_on_mac_linux() + stop_on_mac_linux(exit_on_finish=exit_on_finish) - # Checking Java java_path = check_java_exists() if java_path is None: print("Java not found. Exiting...") - safe_exit(1) + if exit_on_finish: + safe_exit(1) with get_spinner(text=f'Starting Geoweaver...', spinner='dots'): # Start Geoweaver @@ -178,13 +179,15 @@ def start_on_mac_linux(force_restart=True): if counter == max_counter: print("Error: Geoweaver is not up") - safe_exit(1) + if exit_on_finish: + safe_exit(1) else: print("Success: Geoweaver is up") - safe_exit(0) + if exit_on_finish: + safe_exit(0) -def stop_on_mac_linux() -> int: +def stop_on_mac_linux(exit_on_finish: bool=False) -> int: with get_spinner(text=f'Stopping Geoweaver...', spinner='dots'): # Stop running Geoweaver if any logger.info("Stop running Geoweaver if any..") @@ -204,32 +207,26 @@ def stop_on_mac_linux() -> int: return 1 -def start(force_download=False, force_restart=False): +def start(force_download=False, force_restart=False, exit_on_finish=True): download_geoweaver_jar(overwrite=force_download) check_java() if check_os() == 3: logger.debug(f"Detected Windows, running start python script..") - start_on_windows(force_restart) + start_on_windows(force_restart=force_restart, force_download=force_download, exit_on_finish=exit_on_finish) else: logger.debug(f"Detected Linux/MacOs, running start python script..") - start_on_mac_linux(force_restart) - # subprocess.run( - # [f"{get_module_absolute_path()}/start.sh"], cwd=f"{get_root_dir()}/" - # ) + start_on_mac_linux(force_restart=force_restart, force_download=force_download, exit_on_finish=exit_on_finish) -def stop(): +def stop(exit_on_finish: bool=False): check_java() if check_os() == 3: stop_on_windows() else: - # subprocess.run( - # [f"{get_module_absolute_path()}/stop.sh"], - # cwd=f"{get_root_dir()}/", - # shell=True, - # ) - safe_exit(stop_on_mac_linux()) + exit_code = stop_on_mac_linux() + if exit_on_finish: + safe_exit(exit_code) def show(geoweaver_url=GEOWEAVER_DEFAULT_ENDPOINT_URL): diff --git a/pyproject.toml b/pyproject.toml index bd237d9..20b196a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ dependencies = [ "pandas", "halo", "pydantic", + "tabulate", ] [project.urls] diff --git a/test/test_server.py b/test/test_server.py index be94949..83ad59f 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -26,19 +26,19 @@ def test_server_start_stop(self): 200, f"Failed to access URL: {GEOWEAVER_DEFAULT_ENDPOINT_URL}", ) - stop() + stop(exit_on_finish=False) with self.assertRaises(requests.exceptions.ConnectionError): response = requests.get(GEOWEAVER_DEFAULT_ENDPOINT_URL) - stop() # stop again should have no issue + stop(exit_on_finish=False) # stop again should have no issue def test_windows(self): with patch("pygeoweaver.server.checkOS") as mock_checkos: mock_checkos.return_value = 3 with self.assertRaises(RuntimeError): - start() + start(exit_on_finish=False) with self.assertRaises(RuntimeError): - stop() + stop(exit_on_finish=False) def test_show_gui(self): with patch("pygeoweaver.webbrowser.open") as mock_browser_open: