diff --git a/QGIS-Styles/TUFLOW/depth_range_20251222.qml b/QGIS-Styles/TUFLOW/depth_range_20251222.qml new file mode 100644 index 00000000..b39943a2 --- /dev/null +++ b/QGIS-Styles/TUFLOW/depth_range_20251222.qml @@ -0,0 +1,178 @@ + + + + 1 + 1 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + WholeRaster + Estimated + 0.02 + 0.98 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + resamplingFilter + + 0 + diff --git a/dist/ryan_functions-26.1.26.1-py3-none-any.whl b/dist/ryan_functions-26.1.26.1-py3-none-any.whl index 2bdf2b92..359c6adb 100644 Binary files a/dist/ryan_functions-26.1.26.1-py3-none-any.whl and b/dist/ryan_functions-26.1.26.1-py3-none-any.whl differ diff --git a/orchestrators_structure_proposal.txt b/orchestrators_structure_proposal.txt new file mode 100644 index 00000000..3224005b --- /dev/null +++ b/orchestrators_structure_proposal.txt @@ -0,0 +1,58 @@ +Proposed tree (orchestrators grouped by domain) + +ryan_library/ +├── orchestrators/ +│ ├── __init__.py +│ ├── py.typed +│ ├── gdal/ +│ │ ├── __init__.py +│ │ ├── py.typed +│ │ └── gdal_flood_extent.py +│ ├── tuflow/ +│ │ ├── __init__.py +│ │ ├── closure_durations.py +│ │ ├── peak_check_po_csvs.py +│ │ ├── po_combine.py +│ │ ├── pomm_combine.py +│ │ ├── pomm_max_items.py +│ │ ├── tuflow_culverts_merge.py +│ │ ├── tuflow_culverts_mean.py +│ │ ├── tuflow_culverts_timeseries.py +│ │ ├── tuflow_logsummary.py +│ │ ├── tuflow_results_styling.py +│ │ └── tuflow_timeseries_stability.py +│ └── rorb/ +│ ├── __init__.py +│ └── closure_durations.py +├── functions/ +│ └── wrapper_utils.py +└── scripts/ + ├── __init__.py (compat shim -> orchestrators) + ├── py.typed (compat shim package marker) + ├── wrapper_utils.py (compat shim -> functions) + ├── gdal/ + │ ├── __init__.py (compat shim -> orchestrators.gdal) + │ ├── py.typed + │ └── gdal_flood_extent.py (compat shim) + ├── tuflow/ + │ ├── __init__.py (compat shim -> orchestrators.tuflow) + │ ├── closure_durations.py (compat shim) + │ ├── peak_check_po_csvs.py (compat shim) + │ ├── po_combine.py (compat shim) + │ ├── pomm_combine.py (compat shim) + │ ├── pomm_max_items.py (compat shim) + │ ├── tuflow_culverts_merge.py (compat shim) + │ ├── tuflow_culverts_mean.py (compat shim) + │ ├── tuflow_culverts_timeseries.py (compat shim) + │ ├── tuflow_logsummary.py (compat shim) + │ ├── tuflow_results_styling.py (compat shim) + │ └── tuflow_timeseries_stability.py (compat shim) + └── rorb/ + ├── __init__.py (compat shim -> orchestrators.rorb) + └── closure_durations.py (compat shim) + +Notes +- All functional modules live in ryan_library/orchestrators. +- ryan_library/functions/wrapper_utils.py holds shared wrapper utilities. +- ryan_library/scripts becomes compatibility-only until 31/12/2026 with DeprecationWarning notices. +- Orchestrators are grouped by domain (gdal, tuflow, rorb). diff --git a/ryan-scripts/12D-python/thin-raster-terrain-for-12D_v5.py b/ryan-scripts/12D-python/thin-raster-terrain-for-12D_v5.py index 92167dc3..b51e8ea1 100644 --- a/ryan-scripts/12D-python/thin-raster-terrain-for-12D_v5.py +++ b/ryan-scripts/12D-python/thin-raster-terrain-for-12D_v5.py @@ -13,7 +13,7 @@ from loguru import logger from ryan_library.functions.loguru_helpers import setup_logger, worker_initializer from ryan_library.functions.file_utils import ensure_output_directory -from ryan_library.scripts.wrapper_utils import print_library_version +from ryan_library.functions.wrapper_utils import print_library_version def thin_data_by_global_selection(df: pd.DataFrame, thinning_factor: int) -> pd.DataFrame: diff --git a/ryan-scripts/12D-python/tif-to-LAS-valid-only_v6.py b/ryan-scripts/12D-python/tif-to-LAS-valid-only_v6.py index f33c318c..ec97bdfc 100644 --- a/ryan-scripts/12D-python/tif-to-LAS-valid-only_v6.py +++ b/ryan-scripts/12D-python/tif-to-LAS-valid-only_v6.py @@ -6,7 +6,7 @@ import numpy as np from ryan_library.functions.loguru_helpers import setup_logger from ryan_library.functions.terrain_processing import parallel_process_multiple_terrain -from ryan_library.scripts.wrapper_utils import print_library_version +from ryan_library.functions.wrapper_utils import print_library_version def save_tile_las(tile_df, output_dir, base_filename, i, j) -> None: diff --git a/ryan-scripts/RORB-python/RORB-find-closure-durations.py b/ryan-scripts/RORB-python/RORB-find-closure-durations.py index 6d89c3b4..34305e1e 100644 --- a/ryan-scripts/RORB-python/RORB-find-closure-durations.py +++ b/ryan-scripts/RORB-python/RORB-find-closure-durations.py @@ -6,8 +6,8 @@ import os from pathlib import Path -from ryan_library.scripts.RORB.closure_durations import run_closure_durations -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.rorb.closure_durations import run_closure_durations +from ryan_library.functions.wrapper_utils import ( change_working_directory, print_library_version, ) diff --git a/ryan-scripts/TUFLOW-python/LogSummary.py b/ryan-scripts/TUFLOW-python/LogSummary.py index d19acd90..00bd5494 100644 --- a/ryan-scripts/TUFLOW-python/LogSummary.py +++ b/ryan-scripts/TUFLOW-python/LogSummary.py @@ -12,8 +12,8 @@ from pathlib import Path import os -from ryan_library.scripts.tuflow.tuflow_logsummary import main_processing -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.tuflow_logsummary import main_processing +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/TUFLOW-python/POMM-mean-max-aep-dur.py b/ryan-scripts/TUFLOW-python/POMM-mean-max-aep-dur.py index d8f15701..559ba37e 100644 --- a/ryan-scripts/TUFLOW-python/POMM-mean-max-aep-dur.py +++ b/ryan-scripts/TUFLOW-python/POMM-mean-max-aep-dur.py @@ -29,8 +29,8 @@ import gc import os -from ryan_library.scripts.tuflow.pomm_max_items import export_mean_peak_report -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.pomm_max_items import export_mean_peak_report +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, PommPeakWrapperDefaults, add_common_cli_arguments, diff --git a/ryan-scripts/TUFLOW-python/POMM-med-max-aep-dur.py b/ryan-scripts/TUFLOW-python/POMM-med-max-aep-dur.py index 59b64787..242db38c 100644 --- a/ryan-scripts/TUFLOW-python/POMM-med-max-aep-dur.py +++ b/ryan-scripts/TUFLOW-python/POMM-med-max-aep-dur.py @@ -30,8 +30,8 @@ import gc import os -from ryan_library.scripts.tuflow.pomm_max_items import export_median_peak_report -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.pomm_max_items import export_median_peak_report +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, PommPeakWrapperDefaults, add_common_cli_arguments, diff --git a/ryan-scripts/TUFLOW-python/POMM_combine.py b/ryan-scripts/TUFLOW-python/POMM_combine.py index c0f7c447..0406ef23 100644 --- a/ryan-scripts/TUFLOW-python/POMM_combine.py +++ b/ryan-scripts/TUFLOW-python/POMM_combine.py @@ -2,7 +2,7 @@ """ Wrapper Script: Combine TUFLOW POMM Results. -This script acts as a mutable wrapper for `ryan_library.scripts.tuflow.pomm_combine`. +This script acts as a mutable wrapper for `ryan_library.orchestrators.tuflow.pomm_combine`. It manages the combination of "POMM" (Plot Output Maximums/Minimums) data, primarily used for culvert peak analysis. Users can edit hard-coded defaults in this file or use CLI arguments to control the execution. @@ -31,8 +31,8 @@ import gc import os -from ryan_library.scripts.tuflow.pomm_combine import main_processing -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.pomm_combine import main_processing +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/TUFLOW-python/PO_combine.py b/ryan-scripts/TUFLOW-python/PO_combine.py index 320aea22..cdb4b5fd 100644 --- a/ryan-scripts/TUFLOW-python/PO_combine.py +++ b/ryan-scripts/TUFLOW-python/PO_combine.py @@ -2,7 +2,7 @@ """ Wrapper Script: Combine TUFLOW PO Results. -This script acts as a mutable wrapper for `ryan_library.scripts.tuflow.po_combine`. +This script acts as a mutable wrapper for `ryan_library.orchestrators.tuflow.po_combine`. It allows users to hard-code default configurations for merging TUFLOW "PO" (Plot Output) CSV files, while still providing command-line overrides for automation/batch processing. @@ -31,8 +31,8 @@ import gc import os -from ryan_library.scripts.tuflow.po_combine import main_processing -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.po_combine import main_processing +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/TUFLOW-python/TUFLOW-find-closure-durations.py b/ryan-scripts/TUFLOW-python/TUFLOW-find-closure-durations.py index f3732608..44cc1162 100644 --- a/ryan-scripts/TUFLOW-python/TUFLOW-find-closure-durations.py +++ b/ryan-scripts/TUFLOW-python/TUFLOW-find-closure-durations.py @@ -22,8 +22,8 @@ import os -from ryan_library.scripts.tuflow.closure_durations import run_closure_durations -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.closure_durations import run_closure_durations +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/TUFLOW-python/TUFLOW_Culvert-mean-max-aep-dur.py b/ryan-scripts/TUFLOW-python/TUFLOW_Culvert-mean-max-aep-dur.py index 4017346d..d22d4da1 100644 --- a/ryan-scripts/TUFLOW-python/TUFLOW_Culvert-mean-max-aep-dur.py +++ b/ryan-scripts/TUFLOW-python/TUFLOW_Culvert-mean-max-aep-dur.py @@ -23,8 +23,8 @@ import gc import os -from ryan_library.scripts.tuflow.tuflow_culverts_mean import run_culvert_mean_report -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.tuflow_culverts_mean import run_culvert_mean_report +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/TUFLOW-python/TUFLOW_Culvert_Maximums.py b/ryan-scripts/TUFLOW-python/TUFLOW_Culvert_Maximums.py index cb0143cf..eff9c63a 100644 --- a/ryan-scripts/TUFLOW-python/TUFLOW_Culvert_Maximums.py +++ b/ryan-scripts/TUFLOW-python/TUFLOW_Culvert_Maximums.py @@ -21,8 +21,8 @@ import gc import os -from ryan_library.scripts.tuflow.tuflow_culverts_merge import main_processing -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.tuflow_culverts_merge import main_processing +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/TUFLOW-python/TUFLOW_Culvert_Timeseries.py b/ryan-scripts/TUFLOW-python/TUFLOW_Culvert_Timeseries.py index 16267dca..295944f9 100644 --- a/ryan-scripts/TUFLOW-python/TUFLOW_Culvert_Timeseries.py +++ b/ryan-scripts/TUFLOW-python/TUFLOW_Culvert_Timeseries.py @@ -21,8 +21,8 @@ import gc import os -from ryan_library.scripts.tuflow.tuflow_culverts_timeseries import main_processing -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.tuflow_culverts_timeseries import main_processing +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/TUFLOW-python/set_layer_to_filename_v6.py b/ryan-scripts/TUFLOW-python/TUFLOW_GPKG_rename_layer_to_filename.py similarity index 69% rename from ryan-scripts/TUFLOW-python/set_layer_to_filename_v6.py rename to ryan-scripts/TUFLOW-python/TUFLOW_GPKG_rename_layer_to_filename.py index e83bb175..36f554fc 100644 --- a/ryan-scripts/TUFLOW-python/set_layer_to_filename_v6.py +++ b/ryan-scripts/TUFLOW-python/TUFLOW_GPKG_rename_layer_to_filename.py @@ -1,7 +1,8 @@ -# ryan-scripts\TUFLOW-python\set_layer_to_filename_v5.py -# 250913 +# TUFLOW_GPKG_rename_layer_to_filename.py +# 20241011 from collections.abc import Iterator from pathlib import Path +import argparse import geopandas as gpd import pyogrio import shutil @@ -9,6 +10,9 @@ from colorama import init, Fore, Style import sys +SCRIPT_NAME: str = "TUFLOW_GPKG_rename_layer_to_filename" +LAST_UPDATED: str = "2024-10-11" + # === Configuration === # Toggle to search subfolders recursively underneath the Python file. SEARCH_RECURSIVELY: bool = False # Set True to scan all nested folders under the script directory. @@ -42,18 +46,16 @@ def press_any_key(prompt: str = "Press any key to exit...") -> None: pass -# Initialization function to set up environment and return colors -def initialize(target_dir: str | None = None) -> tuple[Path, str, str]: +# Initialization function to set up environment and return colours +def initialize(target_dir: str | None = None) -> tuple[Path, str, str, str]: """ - Returns the base directory to process, plus colours. - - If target_dir is provided, use it (must be a drive-letter path if you plan to chdir). + Returns the base directory plus colours. + - If target_dir is provided, use it (supports drive-letter paths). - Else use the script's folder, keeping the Q: form by using .absolute(), not .resolve(). """ - # Initialize colorama for Windows support init() base: Path = Path(target_dir).expanduser() if target_dir else Path(__file__).absolute().parent - # Return success and failure colors - return base, Fore.GREEN, Fore.RED + return base, Fore.GREEN, Fore.RED, Fore.CYAN # Helper function to check if we should skip the file @@ -67,9 +69,11 @@ def should_skip_file(layers: list[list[str]], gpkg: str, fail_colour: str) -> bo # Helper function to check if renaming is needed -def is_layer_name_correct(old_layer_name: str, new_layer_name: str, gpkg: str, success_colour: str) -> bool: +def is_layer_name_correct( + old_layer_name: str, new_layer_name: str, gpkg: str, skip_colour: str +) -> bool: if old_layer_name == new_layer_name: - print_colored(message=f"Skipping {gpkg} as the layer name is already correct.", color=success_colour) + print_colored(message=f"Skipping {gpkg} as the layer name is already correct.", color=skip_colour) return True return False @@ -105,7 +109,9 @@ def rename_layer_in_geopackage( # Function to process all GeoPackage files in the directory -def process_geopackage_files(base_dir: Path, success_colour: str, fail_colour: str) -> None: +def process_geopackage_files( + base_dir: Path, success_colour: str, fail_colour: str, skip_colour: str +) -> None: # No chdir needed; operate with absolute paths to avoid UNC/cwd issues entirely # Loop over all .gpkg files in the current directory # (If SEARCH_RECURSIVELY is True, this will search all subfolders as well.) @@ -123,7 +129,10 @@ def process_geopackage_files(base_dir: Path, success_colour: str, fail_colour: s new_layer_name: str = gpkg_path.stem old_layer_name: str = layers[0][0] if is_layer_name_correct( - old_layer_name=old_layer_name, new_layer_name=new_layer_name, gpkg=gpkg, success_colour=success_colour + old_layer_name=old_layer_name, + new_layer_name=new_layer_name, + gpkg=gpkg, + skip_colour=skip_colour, ): continue @@ -137,23 +146,43 @@ def process_geopackage_files(base_dir: Path, success_colour: str, fail_colour: s ) -# Main function to execute the script -def main() -> None: - # OPTION A: Use the script's folder (keeps Q: if you launched it that way) - base_dir, success_colour, fail_colour = initialize() +# CLI helpers +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Rename single layers in GeoPackages so the layer matches the file name." + ) + parser.add_argument( + "-t", + "--target", + help="Optional target folder; defaults to the script location when omitted.", + type=str, + ) + return parser.parse_args() - # OPTION B: Explicitly target a folder (drive-letter path); uncomment and set if desired - # base_dir, success_colour, fail_colour = initialize( - # r"Q:\BGER\PER\RP20180.387 WYLOO NANUTARRA ACCESS ROAD - FMG\TUFLOW_Metawandy\model" - # ) - # If you really want to change cwd (not required), only chdir to a drive-letter path: - # if base_dir.drive: # e.g., 'Q:' - # os.chdir(base_dir) +def announce_start(base_dir: Path, target_dir: str | None, info_colour: str) -> None: + target_label = "script folder" if target_dir is None else f"target {target_dir}" + print_colored( + message=( + f"{SCRIPT_NAME} · Last updated {LAST_UPDATED} · working directory {base_dir} ({target_label})" + ), + color=info_colour, + ) + - process_geopackage_files(base_dir=base_dir, success_colour=success_colour, fail_colour=fail_colour) +# Main function to execute the script +def main() -> None: + args = parse_args() + base_dir, success_colour, fail_colour, skip_colour = initialize(args.target) + announce_start(base_dir=base_dir, target_dir=args.target, info_colour=skip_colour) + + process_geopackage_files( + base_dir=base_dir, + success_colour=success_colour, + fail_colour=fail_colour, + skip_colour=skip_colour, + ) - # Wait for user input before exiting (Windows only) if sys.platform == "win32": press_any_key() diff --git a/ryan-scripts/TUFLOW-python/TUFLOW_Results_Styling.py b/ryan-scripts/TUFLOW-python/TUFLOW_Results_Styling.py index 9625c8b0..f31be617 100644 --- a/ryan-scripts/TUFLOW-python/TUFLOW_Results_Styling.py +++ b/ryan-scripts/TUFLOW-python/TUFLOW_Results_Styling.py @@ -2,7 +2,7 @@ """ Wrapper Script: TUFLOW Results Styling. -This script acts as a mutable wrapper for `ryan_library.scripts.tuflow.tuflow_results_styling`. +This script acts as a mutable wrapper for `ryan_library.orchestrators.tuflow.tuflow_results_styling`. It applies QGIS styles (.qml) to TUFLOW results (rasters/vectors) found in the target directory. Users can define custom QML overrides in the `user_qml_overrides` dictionary within this file. """ @@ -14,13 +14,13 @@ from loguru import logger from ryan_library.functions.loguru_helpers import setup_logger -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.functions.wrapper_utils import ( change_working_directory, print_library_version, ) # Now import the TUFLOWResultsStyler class -from ryan_library.scripts.tuflow.tuflow_results_styling import TUFLOWResultsStyler +from ryan_library.orchestrators.tuflow.tuflow_results_styling import TUFLOWResultsStyler # User Overrides: Define your custom QML paths here user_qml_overrides: dict[str, str] = { diff --git a/ryan-scripts/TUFLOW-python/TUFLOW_Timeseries_Peaks_Check.py b/ryan-scripts/TUFLOW-python/TUFLOW_Timeseries_Peaks_Check.py index ee6f1005..c95a8ef1 100644 --- a/ryan-scripts/TUFLOW-python/TUFLOW_Timeseries_Peaks_Check.py +++ b/ryan-scripts/TUFLOW-python/TUFLOW_Timeseries_Peaks_Check.py @@ -3,7 +3,7 @@ Wrapper Script: Peak checks for TUFLOW PO CSV files. This wrapper exposes hard-coded defaults for quick edits while delegating the heavy -lifting to ``ryan_library.scripts.tuflow.peak_check_po_csvs``. +lifting to ``ryan_library.orchestrators.tuflow.peak_check_po_csvs``. """ from pathlib import Path @@ -12,8 +12,8 @@ import gc import os -from ryan_library.scripts.tuflow.peak_check_po_csvs import main_processing -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.peak_check_po_csvs import main_processing +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/TUFLOW-python/TUFLOW_Timeseries_Stability.py b/ryan-scripts/TUFLOW-python/TUFLOW_Timeseries_Stability.py index 73efe1d7..e30abd95 100644 --- a/ryan-scripts/TUFLOW-python/TUFLOW_Timeseries_Stability.py +++ b/ryan-scripts/TUFLOW-python/TUFLOW_Timeseries_Stability.py @@ -11,8 +11,8 @@ import gc import os -from ryan_library.scripts.tuflow.tuflow_timeseries_stability import main_processing -from ryan_library.scripts.wrapper_utils import ( +from ryan_library.orchestrators.tuflow.tuflow_timeseries_stability import main_processing +from ryan_library.functions.wrapper_utils import ( CommonWrapperOptions, add_common_cli_arguments, change_working_directory, diff --git a/ryan-scripts/gdal-python/GDAL_Flood_Extent.py b/ryan-scripts/gdal-python/GDAL_Flood_Extent.py index 40b4f673..035a1e43 100644 --- a/ryan-scripts/gdal-python/GDAL_Flood_Extent.py +++ b/ryan-scripts/gdal-python/GDAL_Flood_Extent.py @@ -3,7 +3,7 @@ from pathlib import Path import os import sys -from ryan_library.scripts.gdal.gdal_flood_extent import main_processing +from ryan_library.orchestrators.gdal.gdal_flood_extent import main_processing def main(): diff --git a/ryan-scripts/misc-python/ss/RFFE_Only_v5.py b/ryan-scripts/misc-python/ss/RFFE_Only_v5.py index 85cd1d94..f8b0633e 100644 --- a/ryan-scripts/misc-python/ss/RFFE_Only_v5.py +++ b/ryan-scripts/misc-python/ss/RFFE_Only_v5.py @@ -23,7 +23,7 @@ from pandas import DataFrame from ryan_library.functions.loguru_helpers import setup_logger from loguru import logger -from ryan_library.scripts.wrapper_utils import print_library_version +from ryan_library.functions.wrapper_utils import print_library_version # ─── Constants ──────────────────────────────────────────────────────────────── DEFAULT_INPUT_DIR: Final[Path] = Path(r"C:\Temp\tester") diff --git a/ryan_library/__init__.py b/ryan_library/__init__.py index 8e7a251a..f325e64e 100644 --- a/ryan_library/__init__.py +++ b/ryan_library/__init__.py @@ -1,3 +1,4 @@ from .functions import * +from .orchestrators import * from .scripts import * from .processors import * diff --git a/ryan_library/functions/tuflow/notebook_helpers.py b/ryan_library/functions/tuflow/notebook_helpers.py index 81d9730d..affce972 100644 --- a/ryan_library/functions/tuflow/notebook_helpers.py +++ b/ryan_library/functions/tuflow/notebook_helpers.py @@ -11,7 +11,7 @@ from ryan_library.functions.tuflow.tuflow_common import collect_files, process_files_in_parallel, process_file from ryan_library.processors.tuflow.base_processor import BaseProcessor from ryan_library.processors.tuflow.processor_collection import ProcessorCollection -from ryan_library.scripts.tuflow.tuflow_culverts_mean import find_culvert_aep_dur_mean, find_culvert_aep_mean_max +from ryan_library.orchestrators.tuflow.tuflow_culverts_mean import find_culvert_aep_dur_mean, find_culvert_aep_mean_max import pandas as pd import matplotlib.pyplot as plt diff --git a/ryan_library/functions/wrapper_utils.py b/ryan_library/functions/wrapper_utils.py new file mode 100644 index 00000000..b88bb9cb --- /dev/null +++ b/ryan_library/functions/wrapper_utils.py @@ -0,0 +1,146 @@ +"""Utility functions shared by wrapper scripts.""" + +from argparse import ArgumentParser, Namespace +from collections.abc import Collection +from dataclasses import dataclass +import os +from pathlib import Path +from typing import Protocol, Sequence +from importlib.metadata import PackageNotFoundError, version + + +@dataclass(slots=True) +class CommonWrapperOptions: + """Container for CLI-provided overrides that most wrappers share.""" + + console_log_level: str | None = None + data_types: tuple[str, ...] | None = None + locations_to_include: tuple[str, ...] | None = None + working_directory: Path | None = None + + +class PeakReportExporter(Protocol): + """Callable signature for POMM peak report exporters.""" + + def __call__( + self, + *, + script_directory: Path, + log_level: str, + include_pomm: bool, + locations_to_include: Collection[str] | None, + include_data_types: Collection[str] | None, + ) -> None: ... + + +@dataclass(slots=True, frozen=True) +class PommPeakWrapperDefaults: + """Default configuration values for POMM peak report wrappers.""" + + console_log_level: str + include_pomm: bool + include_data_types: tuple[str, ...] + locations_to_include: tuple[str, ...] + working_directory: Path + + +def change_working_directory(target_dir: Path) -> bool: + """Change the working directory and handle failures.""" + try: + os.chdir(target_dir) + print(f"Current Working Directory: {Path.cwd()}") + except OSError as exc: + print(f"Failed to change working directory to {target_dir}: {exc}") + if os.name == "nt": + os.system("PAUSE") + return False + return True + + +def print_library_version(package_name: str = "ryan_functions") -> None: + """Display the installed version of *package_name* if available.""" + try: + print(f"{package_name} version: {version(distribution_name=package_name)}") + except PackageNotFoundError: + print(f"{package_name} version: unknown") + + +def add_common_cli_arguments(parser: ArgumentParser) -> None: + """Inject shared CLI arguments that most wrappers support.""" + parser.add_argument( + "--console-log-level", + dest="console_log_level", + help="Set log verbosity (e.g., INFO or DEBUG). Defaults to the script value.", + ) + parser.add_argument( + "--data-types", + nargs="+", + metavar="TYPE", + help="Override the data types to load (e.g., POMM RLL_Qmx). Defaults to the script value.", + ) + parser.add_argument( + "--locations", + nargs="+", + metavar="LOCATION", + help="Limit processing to one or more PO/Location/Channel identifiers.", + ) + parser.add_argument( + "--working-directory", + type=Path, + help="Directory to process instead of the script's location.", + ) + + +def parse_common_cli_arguments(args: Namespace) -> CommonWrapperOptions: + """Map argparse results to :class:`CommonWrapperOptions`.""" + locations_argument = getattr(args, "locations", None) + data_types_argument = getattr(args, "data_types", None) + return CommonWrapperOptions( + console_log_level=getattr(args, "console_log_level", None), + data_types=_coerce_sequence_argument(raw_values=data_types_argument), + locations_to_include=_coerce_locations_argument(raw_locations=locations_argument), + working_directory=getattr(args, "working_directory", None), + ) + + +def run_pomm_peak_report_wrapper( + *, + exporter: PeakReportExporter, + defaults: PommPeakWrapperDefaults, + overrides: CommonWrapperOptions, +) -> None: + """Run a POMM peak report wrapper using common defaults and CLI overrides.""" + print_library_version() + + script_directory: Path = overrides.working_directory or defaults.working_directory + if not change_working_directory(target_dir=script_directory): + return + + effective_console_log_level: str = overrides.console_log_level or defaults.console_log_level + effective_data_types: tuple[str, ...] | None = overrides.data_types or defaults.include_data_types or None + effective_locations: tuple[str, ...] | None = ( + overrides.locations_to_include if overrides.locations_to_include else (defaults.locations_to_include or None) + ) + + exporter( + script_directory=script_directory, + log_level=effective_console_log_level, + include_pomm=defaults.include_pomm, + locations_to_include=effective_locations, + include_data_types=list(effective_data_types) if effective_data_types else None, + ) + print() + print_library_version() + + +def _coerce_locations_argument( + raw_locations: Sequence[str] | None, +) -> tuple[str, ...] | None: + return _coerce_sequence_argument(raw_values=raw_locations) + + +def _coerce_sequence_argument(raw_values: Sequence[str] | None) -> tuple[str, ...] | None: + if not raw_values: + return None + normalized: tuple[str, ...] = tuple(value.strip() for value in raw_values if value.strip()) + return normalized or None diff --git a/ryan_library/orchestrators/__init__.py b/ryan_library/orchestrators/__init__.py new file mode 100644 index 00000000..367b555d --- /dev/null +++ b/ryan_library/orchestrators/__init__.py @@ -0,0 +1,7 @@ +"""Orchestrators that coordinate library workflows.""" + +from . import gdal +from . import rorb +from . import tuflow + +__all__: list[str] = ["gdal", "rorb", "tuflow"] diff --git a/ryan_library/orchestrators/gdal/__init__.py b/ryan_library/orchestrators/gdal/__init__.py new file mode 100644 index 00000000..a7e4957a --- /dev/null +++ b/ryan_library/orchestrators/gdal/__init__.py @@ -0,0 +1 @@ +"""GDAL orchestrators.""" diff --git a/ryan_library/scripts/gdal/gdal_flood_extent.py b/ryan_library/orchestrators/gdal/gdal_flood_extent.py similarity index 98% rename from ryan_library/scripts/gdal/gdal_flood_extent.py rename to ryan_library/orchestrators/gdal/gdal_flood_extent.py index 300cb54c..67fb3089 100644 --- a/ryan_library/scripts/gdal/gdal_flood_extent.py +++ b/ryan_library/orchestrators/gdal/gdal_flood_extent.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/gdal/gdal_flood_extent.py +# ryan_library/orchestrators/gdal/gdal_flood_extent.py import os from pathlib import Path diff --git a/ryan_library/scripts/RORB/__init__.py b/ryan_library/orchestrators/gdal/py.typed similarity index 100% rename from ryan_library/scripts/RORB/__init__.py rename to ryan_library/orchestrators/gdal/py.typed diff --git a/ryan_library/orchestrators/py.typed b/ryan_library/orchestrators/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/ryan_library/orchestrators/rorb/__init__.py b/ryan_library/orchestrators/rorb/__init__.py new file mode 100644 index 00000000..5d78e240 --- /dev/null +++ b/ryan_library/orchestrators/rorb/__init__.py @@ -0,0 +1 @@ +"""RORB orchestrators.""" diff --git a/ryan_library/scripts/RORB/closure_durations.py b/ryan_library/orchestrators/rorb/closure_durations.py similarity index 98% rename from ryan_library/scripts/RORB/closure_durations.py rename to ryan_library/orchestrators/rorb/closure_durations.py index 68a9310c..c161131a 100644 --- a/ryan_library/scripts/RORB/closure_durations.py +++ b/ryan_library/orchestrators/rorb/closure_durations.py @@ -1,4 +1,4 @@ -# ryan_library\scripts\RORB\closure_durations.py +# ryan_library\orchestrators\rorb\closure_durations.py from datetime import datetime from pathlib import Path diff --git a/ryan_library/orchestrators/tuflow/__init__.py b/ryan_library/orchestrators/tuflow/__init__.py new file mode 100644 index 00000000..bd006804 --- /dev/null +++ b/ryan_library/orchestrators/tuflow/__init__.py @@ -0,0 +1 @@ +"""TUFLOW orchestrators.""" diff --git a/ryan_library/scripts/tuflow/closure_durations.py b/ryan_library/orchestrators/tuflow/closure_durations.py similarity index 98% rename from ryan_library/scripts/tuflow/closure_durations.py rename to ryan_library/orchestrators/tuflow/closure_durations.py index 9c604503..f6376e41 100644 --- a/ryan_library/scripts/tuflow/closure_durations.py +++ b/ryan_library/orchestrators/tuflow/closure_durations.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/tuflow/closure_durations.py +# ryan_library/orchestrators/tuflow/closure_durations.py """Orchestrator for computing closure durations from TUFLOW PO processors. This script: diff --git a/ryan_library/scripts/tuflow/peak_check_po_csvs.py b/ryan_library/orchestrators/tuflow/peak_check_po_csvs.py similarity index 98% rename from ryan_library/scripts/tuflow/peak_check_po_csvs.py rename to ryan_library/orchestrators/tuflow/peak_check_po_csvs.py index 3d9272bd..ab079ada 100644 --- a/ryan_library/scripts/tuflow/peak_check_po_csvs.py +++ b/ryan_library/orchestrators/tuflow/peak_check_po_csvs.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/tuflow/peak_check_po_csvs.py +# ryan_library/orchestrators/tuflow/peak_check_po_csvs.py """ Peak checks for TUFLOW PO timeseries CSVs. diff --git a/ryan_library/scripts/tuflow/po_combine.py b/ryan_library/orchestrators/tuflow/po_combine.py similarity index 100% rename from ryan_library/scripts/tuflow/po_combine.py rename to ryan_library/orchestrators/tuflow/po_combine.py diff --git a/ryan_library/scripts/tuflow/pomm_combine.py b/ryan_library/orchestrators/tuflow/pomm_combine.py similarity index 100% rename from ryan_library/scripts/tuflow/pomm_combine.py rename to ryan_library/orchestrators/tuflow/pomm_combine.py diff --git a/ryan_library/scripts/tuflow/pomm_max_items.py b/ryan_library/orchestrators/tuflow/pomm_max_items.py similarity index 99% rename from ryan_library/scripts/tuflow/pomm_max_items.py rename to ryan_library/orchestrators/tuflow/pomm_max_items.py index b6aad58b..fc9bcabe 100644 --- a/ryan_library/scripts/tuflow/pomm_max_items.py +++ b/ryan_library/orchestrators/tuflow/pomm_max_items.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/pomm_max_items.py +# ryan_library/orchestrators/pomm_max_items.py """ POMM Peak Reporting Utilities. diff --git a/ryan_library/scripts/tuflow/tuflow_culverts_mean.py b/ryan_library/orchestrators/tuflow/tuflow_culverts_mean.py similarity index 100% rename from ryan_library/scripts/tuflow/tuflow_culverts_mean.py rename to ryan_library/orchestrators/tuflow/tuflow_culverts_mean.py diff --git a/ryan_library/scripts/tuflow/tuflow_culverts_merge.py b/ryan_library/orchestrators/tuflow/tuflow_culverts_merge.py similarity index 98% rename from ryan_library/scripts/tuflow/tuflow_culverts_merge.py rename to ryan_library/orchestrators/tuflow/tuflow_culverts_merge.py index 49a0cd28..e6342c47 100644 --- a/ryan_library/scripts/tuflow/tuflow_culverts_merge.py +++ b/ryan_library/orchestrators/tuflow/tuflow_culverts_merge.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/tuflow/tuflow_culverts_merge.py +# ryan_library/orchestrators/tuflow/tuflow_culverts_merge.py """ Merge TUFLOW Culvert Maximums. diff --git a/ryan_library/scripts/tuflow/tuflow_culverts_timeseries.py b/ryan_library/orchestrators/tuflow/tuflow_culverts_timeseries.py similarity index 98% rename from ryan_library/scripts/tuflow/tuflow_culverts_timeseries.py rename to ryan_library/orchestrators/tuflow/tuflow_culverts_timeseries.py index 2b1583dc..8c8a58cc 100644 --- a/ryan_library/scripts/tuflow/tuflow_culverts_timeseries.py +++ b/ryan_library/orchestrators/tuflow/tuflow_culverts_timeseries.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/tuflow/tuflow_culverts_timeseries.py +# ryan_library/orchestrators/tuflow/tuflow_culverts_timeseries.py """ Merge TUFLOW Culvert Timeseries. diff --git a/ryan_library/scripts/tuflow/tuflow_logsummary.py b/ryan_library/orchestrators/tuflow/tuflow_logsummary.py similarity index 99% rename from ryan_library/scripts/tuflow/tuflow_logsummary.py rename to ryan_library/orchestrators/tuflow/tuflow_logsummary.py index d3c2e9bf..498b6a47 100644 --- a/ryan_library/scripts/tuflow/tuflow_logsummary.py +++ b/ryan_library/orchestrators/tuflow/tuflow_logsummary.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/tuflow/tuflow_logsummary.py +# ryan_library/orchestrators/tuflow/tuflow_logsummary.py """ TUFLOW Log Summary. diff --git a/ryan_library/scripts/tuflow/tuflow_results_styling.py b/ryan_library/orchestrators/tuflow/tuflow_results_styling.py similarity index 98% rename from ryan_library/scripts/tuflow/tuflow_results_styling.py rename to ryan_library/orchestrators/tuflow/tuflow_results_styling.py index 2233e196..2ea9a25b 100644 --- a/ryan_library/scripts/tuflow/tuflow_results_styling.py +++ b/ryan_library/orchestrators/tuflow/tuflow_results_styling.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/tuflow/tuflow_results_styling.py +# ryan_library/orchestrators/tuflow/tuflow_results_styling.py """ TUFLOW Results Styling. @@ -43,7 +43,7 @@ class TUFLOWResultsStyler: def __init__(self, user_qml_overrides: dict[str, str] | None = None) -> None: """Initializes the TUFLOWResultsStyler with default styles path and user overrides.""" - # __file__ resolves to ryan_library/scripts/tuflow/tuflow_results_styling.py + # __file__ resolves to ryan_library/orchestrators/tuflow/tuflow_results_styling.py # We need the repository root to locate QML files under QGIS-Styles/TUFLOW self.default_styles_path: Path = Path(__file__).absolute().parents[3] / "QGIS-Styles" / "TUFLOW" logger.debug("Default styles path: {}", self.default_styles_path) @@ -158,7 +158,7 @@ def process_gpkg(self, filename: str, layer_name: str, current_path: Path, qml_p ) styles: list[tuple[str, str]] = cursor.fetchall() - for style_name, style_qml in styles: + for style_name, _style_qml in styles: logger.info( f"Applying style '{style_name}' to layer '{layer_name}'", ) diff --git a/ryan_library/scripts/tuflow/tuflow_timeseries_stability.py b/ryan_library/orchestrators/tuflow/tuflow_timeseries_stability.py similarity index 98% rename from ryan_library/scripts/tuflow/tuflow_timeseries_stability.py rename to ryan_library/orchestrators/tuflow/tuflow_timeseries_stability.py index cb222076..00d5f4cb 100644 --- a/ryan_library/scripts/tuflow/tuflow_timeseries_stability.py +++ b/ryan_library/orchestrators/tuflow/tuflow_timeseries_stability.py @@ -1,4 +1,4 @@ -# ryan_library/scripts/tuflow/tuflow_timeseries_stability.py +# ryan_library/orchestrators/tuflow/tuflow_timeseries_stability.py """ Timeseries stability checks for TUFLOW outputs. diff --git a/ryan_library/scripts/__init__.py b/ryan_library/scripts/__init__.py index 69131995..3679ba0e 100644 --- a/ryan_library/scripts/__init__.py +++ b/ryan_library/scripts/__init__.py @@ -1,10 +1,43 @@ -"""Convenience imports for script entry points.""" +"""Compatibility imports for legacy script entry points.""" + +from __future__ import annotations -from importlib import import_module import sys -# Expose relocated TUFLOW scripts at this package level for backward compatibility -_tuflow_modules = [ +from ryan_library.orchestrators import gdal as gdal +from ryan_library.orchestrators import rorb as rorb +from ryan_library.orchestrators.rorb import closure_durations as rorb_closure_durations +from ryan_library.orchestrators import tuflow as tuflow +from ryan_library.orchestrators.tuflow import closure_durations as closure_durations +from ryan_library.orchestrators.tuflow import pomm_combine as pomm_combine +from ryan_library.orchestrators.tuflow import tuflow_culverts_merge as tuflow_culverts_merge +from ryan_library.orchestrators.tuflow import tuflow_culverts_timeseries as tuflow_culverts_timeseries +from ryan_library.orchestrators.tuflow import tuflow_logsummary as tuflow_logsummary +from ryan_library.orchestrators.tuflow import tuflow_results_styling as tuflow_results_styling +from ryan_library.scripts._compat import warn_deprecated + +warn_deprecated("ryan_library.scripts", "ryan_library.orchestrators") + +RORB = rorb + +sys.modules[f"{__name__}.gdal"] = gdal +sys.modules[f"{__name__}.tuflow"] = tuflow +sys.modules[f"{__name__}.rorb"] = rorb +sys.modules[f"{__name__}.RORB"] = rorb +sys.modules[f"{__name__}.RORB.closure_durations"] = rorb_closure_durations + +sys.modules[f"{__name__}.tuflow_culverts_merge"] = tuflow_culverts_merge +sys.modules[f"{__name__}.tuflow_culverts_timeseries"] = tuflow_culverts_timeseries +sys.modules[f"{__name__}.tuflow_logsummary"] = tuflow_logsummary +sys.modules[f"{__name__}.tuflow_results_styling"] = tuflow_results_styling +sys.modules[f"{__name__}.pomm_combine"] = pomm_combine +sys.modules[f"{__name__}.closure_durations"] = closure_durations + +__all__ = [ + "gdal", + "tuflow", + "rorb", + "RORB", "tuflow_culverts_merge", "tuflow_culverts_timeseries", "tuflow_logsummary", @@ -12,10 +45,3 @@ "pomm_combine", "closure_durations", ] - -for _mod in _tuflow_modules: - module = import_module(f"{__name__}.tuflow.{_mod}") - sys.modules[f"{__name__}.{_mod}"] = module - globals()[_mod] = module - -__all__ = list(_tuflow_modules) diff --git a/ryan_library/scripts/_compat.py b/ryan_library/scripts/_compat.py new file mode 100644 index 00000000..cf0950e6 --- /dev/null +++ b/ryan_library/scripts/_compat.py @@ -0,0 +1,20 @@ +"""Compatibility helpers for deprecated script imports.""" + +from __future__ import annotations + +import warnings + +_DEPRECATION_DATE = "31/12/2026" + + +def warn_deprecated(module_name: str, replacement: str) -> None: + """Emit a deprecation warning for the legacy scripts namespace.""" + warnings.warn( + ( + f"{module_name} is deprecated; use {replacement}. " + f"Backwards compatibility is supported until {_DEPRECATION_DATE}." + f"Get the latest wrapper from ryan-tools\ryan-scripts\\ " + ), + DeprecationWarning, + stacklevel=2, + ) diff --git a/ryan_library/scripts/gdal/__init__.py b/ryan_library/scripts/gdal/__init__.py index e69de29b..066cc3d6 100644 --- a/ryan_library/scripts/gdal/__init__.py +++ b/ryan_library/scripts/gdal/__init__.py @@ -0,0 +1,8 @@ +"""Compatibility package for GDAL orchestrators.""" + +from ryan_library.orchestrators.gdal import gdal_flood_extent as gdal_flood_extent +from ryan_library.scripts._compat import warn_deprecated + +warn_deprecated("ryan_library.scripts.gdal", "ryan_library.orchestrators.gdal") + +__all__ = ["gdal_flood_extent"] diff --git a/ryan_library/scripts/pomm_max_items.py b/ryan_library/scripts/pomm_max_items.py index 13f21169..e296709e 100644 --- a/ryan_library/scripts/pomm_max_items.py +++ b/ryan_library/scripts/pomm_max_items.py @@ -2,7 +2,14 @@ """Compatibility wrapper that delegates to the TUFLOW POMM orchestrator. This shim keeps older imports working now that the implementation lives under -``ryan_library.scripts.tuflow.pomm_max_items`` with the other TUFLOW orchestrators. +``ryan_library.orchestrators.tuflow.pomm_max_items`` with the other TUFLOW orchestrators. """ -from ryan_library.scripts.tuflow.pomm_max_items import * # noqa: F401,F403 +from ryan_library.scripts._compat import warn_deprecated + +warn_deprecated( + "ryan_library.scripts.pomm_max_items", + "ryan_library.orchestrators.tuflow.pomm_max_items", +) + +from ryan_library.orchestrators.tuflow.pomm_max_items import * # noqa: F401,F403 diff --git a/ryan_library/scripts/rorb/__init__.py b/ryan_library/scripts/rorb/__init__.py new file mode 100644 index 00000000..246c0bc0 --- /dev/null +++ b/ryan_library/scripts/rorb/__init__.py @@ -0,0 +1,8 @@ +"""Compatibility package for RORB orchestrators.""" + +from ryan_library.orchestrators.rorb import closure_durations as closure_durations +from ryan_library.scripts._compat import warn_deprecated + +warn_deprecated("ryan_library.scripts.rorb", "ryan_library.orchestrators.rorb") + +__all__ = ["closure_durations"] diff --git a/ryan_library/scripts/tuflow/__init__.py b/ryan_library/scripts/tuflow/__init__.py index 8b137891..454712da 100644 --- a/ryan_library/scripts/tuflow/__init__.py +++ b/ryan_library/scripts/tuflow/__init__.py @@ -1 +1,30 @@ +"""Compatibility package for TUFLOW orchestrators.""" +from ryan_library.orchestrators.tuflow import closure_durations as closure_durations +from ryan_library.orchestrators.tuflow import peak_check_po_csvs as peak_check_po_csvs +from ryan_library.orchestrators.tuflow import po_combine as po_combine +from ryan_library.orchestrators.tuflow import pomm_combine as pomm_combine +from ryan_library.orchestrators.tuflow import pomm_max_items as pomm_max_items +from ryan_library.orchestrators.tuflow import tuflow_culverts_mean as tuflow_culverts_mean +from ryan_library.orchestrators.tuflow import tuflow_culverts_merge as tuflow_culverts_merge +from ryan_library.orchestrators.tuflow import tuflow_culverts_timeseries as tuflow_culverts_timeseries +from ryan_library.orchestrators.tuflow import tuflow_logsummary as tuflow_logsummary +from ryan_library.orchestrators.tuflow import tuflow_results_styling as tuflow_results_styling +from ryan_library.orchestrators.tuflow import tuflow_timeseries_stability as tuflow_timeseries_stability +from ryan_library.scripts._compat import warn_deprecated + +warn_deprecated("ryan_library.scripts.tuflow", "ryan_library.orchestrators.tuflow") + +__all__ = [ + "closure_durations", + "peak_check_po_csvs", + "po_combine", + "pomm_combine", + "pomm_max_items", + "tuflow_culverts_mean", + "tuflow_culverts_merge", + "tuflow_culverts_timeseries", + "tuflow_logsummary", + "tuflow_results_styling", + "tuflow_timeseries_stability", +] diff --git a/ryan_library/scripts/wrapper_utils.py b/ryan_library/scripts/wrapper_utils.py index b88bb9cb..be8314fc 100644 --- a/ryan_library/scripts/wrapper_utils.py +++ b/ryan_library/scripts/wrapper_utils.py @@ -1,146 +1,7 @@ -"""Utility functions shared by wrapper scripts.""" +"""Compatibility wrapper for shared wrapper utilities.""" -from argparse import ArgumentParser, Namespace -from collections.abc import Collection -from dataclasses import dataclass -import os -from pathlib import Path -from typing import Protocol, Sequence -from importlib.metadata import PackageNotFoundError, version +from ryan_library.scripts._compat import warn_deprecated +warn_deprecated("ryan_library.scripts.wrapper_utils", "ryan_library.functions.wrapper_utils") -@dataclass(slots=True) -class CommonWrapperOptions: - """Container for CLI-provided overrides that most wrappers share.""" - - console_log_level: str | None = None - data_types: tuple[str, ...] | None = None - locations_to_include: tuple[str, ...] | None = None - working_directory: Path | None = None - - -class PeakReportExporter(Protocol): - """Callable signature for POMM peak report exporters.""" - - def __call__( - self, - *, - script_directory: Path, - log_level: str, - include_pomm: bool, - locations_to_include: Collection[str] | None, - include_data_types: Collection[str] | None, - ) -> None: ... - - -@dataclass(slots=True, frozen=True) -class PommPeakWrapperDefaults: - """Default configuration values for POMM peak report wrappers.""" - - console_log_level: str - include_pomm: bool - include_data_types: tuple[str, ...] - locations_to_include: tuple[str, ...] - working_directory: Path - - -def change_working_directory(target_dir: Path) -> bool: - """Change the working directory and handle failures.""" - try: - os.chdir(target_dir) - print(f"Current Working Directory: {Path.cwd()}") - except OSError as exc: - print(f"Failed to change working directory to {target_dir}: {exc}") - if os.name == "nt": - os.system("PAUSE") - return False - return True - - -def print_library_version(package_name: str = "ryan_functions") -> None: - """Display the installed version of *package_name* if available.""" - try: - print(f"{package_name} version: {version(distribution_name=package_name)}") - except PackageNotFoundError: - print(f"{package_name} version: unknown") - - -def add_common_cli_arguments(parser: ArgumentParser) -> None: - """Inject shared CLI arguments that most wrappers support.""" - parser.add_argument( - "--console-log-level", - dest="console_log_level", - help="Set log verbosity (e.g., INFO or DEBUG). Defaults to the script value.", - ) - parser.add_argument( - "--data-types", - nargs="+", - metavar="TYPE", - help="Override the data types to load (e.g., POMM RLL_Qmx). Defaults to the script value.", - ) - parser.add_argument( - "--locations", - nargs="+", - metavar="LOCATION", - help="Limit processing to one or more PO/Location/Channel identifiers.", - ) - parser.add_argument( - "--working-directory", - type=Path, - help="Directory to process instead of the script's location.", - ) - - -def parse_common_cli_arguments(args: Namespace) -> CommonWrapperOptions: - """Map argparse results to :class:`CommonWrapperOptions`.""" - locations_argument = getattr(args, "locations", None) - data_types_argument = getattr(args, "data_types", None) - return CommonWrapperOptions( - console_log_level=getattr(args, "console_log_level", None), - data_types=_coerce_sequence_argument(raw_values=data_types_argument), - locations_to_include=_coerce_locations_argument(raw_locations=locations_argument), - working_directory=getattr(args, "working_directory", None), - ) - - -def run_pomm_peak_report_wrapper( - *, - exporter: PeakReportExporter, - defaults: PommPeakWrapperDefaults, - overrides: CommonWrapperOptions, -) -> None: - """Run a POMM peak report wrapper using common defaults and CLI overrides.""" - print_library_version() - - script_directory: Path = overrides.working_directory or defaults.working_directory - if not change_working_directory(target_dir=script_directory): - return - - effective_console_log_level: str = overrides.console_log_level or defaults.console_log_level - effective_data_types: tuple[str, ...] | None = overrides.data_types or defaults.include_data_types or None - effective_locations: tuple[str, ...] | None = ( - overrides.locations_to_include if overrides.locations_to_include else (defaults.locations_to_include or None) - ) - - exporter( - script_directory=script_directory, - log_level=effective_console_log_level, - include_pomm=defaults.include_pomm, - locations_to_include=effective_locations, - include_data_types=list(effective_data_types) if effective_data_types else None, - ) - print() - print_library_version() - - -def _coerce_locations_argument( - raw_locations: Sequence[str] | None, -) -> tuple[str, ...] | None: - return _coerce_sequence_argument(raw_values=raw_locations) - - -def _coerce_sequence_argument(raw_values: Sequence[str] | None) -> tuple[str, ...] | None: - if not raw_values: - return None - normalized: tuple[str, ...] = tuple(value.strip() for value in raw_values if value.strip()) - return normalized or None +from ryan_library.functions.wrapper_utils import * # noqa: F401,F403