diff --git a/las_geoh5/export_directories/uijson.py b/las_geoh5/export_directories/uijson.py index b9db22d..b32eb96 100644 --- a/las_geoh5/export_directories/uijson.py +++ b/las_geoh5/export_directories/uijson.py @@ -6,32 +6,30 @@ # (see LICENSE file at the root of this source code package). # +from copy import deepcopy -ui_json = { - "title": "Drillhole group to LAS file directories", - "geoh5": None, - "run_command": "las_geoh5.export_directories.driver", - "run_command_boolean": { - "value": False, - "label": "Run python module", - "main": True, - "tooltip": "Warning: launches process to run python model on save", - }, - "monitoring_directory": None, - "conda_environment": "las-geoh5", - "conda_environment_boolean": False, - "workspace": None, - "drillhole_group": { - "main": True, - "label": "Drillhole group", - "value": None, - "groupType": ["{825424fb-c2c6-4fea-9f2b-6cd00023d393}"], - }, - "name": { - "main": True, - "label": "Property group name", - "value": None, - "optional": True, - "enabled": False, +from geoh5py.ui_json.constants import default_ui_json + +# pylint: disable=duplicate-code + +ui_json = dict( + deepcopy(default_ui_json), + **{ + "title": "Drillhole group to LAS file directories", + "run_command": "las_geoh5.export_directories.driver", + "conda_environment": "las-geoh5", + "drillhole_group": { + "main": True, + "label": "Drillhole group", + "value": None, + "groupType": ["{825424fb-c2c6-4fea-9f2b-6cd00023d393}"], + }, + "name": { + "main": True, + "label": "Property group name", + "value": None, + "optional": True, + "enabled": False, + }, }, -} +) diff --git a/las_geoh5/import_directories/uijson.py b/las_geoh5/import_directories/uijson.py index ed964f2..47eec9f 100644 --- a/las_geoh5/import_directories/uijson.py +++ b/las_geoh5/import_directories/uijson.py @@ -15,5 +15,6 @@ **{ "title": "LAS file directories to Drillhole group", "run_command": "las_geoh5.import_directories.driver", + "conda_environment": "las-geoh5", } ) diff --git a/las_geoh5/import_files/driver.py b/las_geoh5/import_files/driver.py index 8962ed0..f987794 100644 --- a/las_geoh5/import_files/driver.py +++ b/las_geoh5/import_files/driver.py @@ -24,17 +24,40 @@ from las_geoh5.import_files.params import ImportOptions, NameOptions from las_geoh5.import_las import las_to_drillhole -logger = logging.getLogger("Import Files") -logger.setLevel(logging.INFO) -stream_handler = logging.StreamHandler() -file_handler = logging.FileHandler("import_las_files.log") -formatter = logging.Formatter("%(asctime)s : %(name)s : %(levelname)s : %(message)s") -file_handler.setFormatter(formatter) -stream_handler.setFormatter(formatter) -file_handler.setLevel(logging.INFO) -stream_handler.setLevel(logging.INFO) -logger.addHandler(file_handler) -logger.addHandler(stream_handler) + +def get_logger( + name: str, level: int = logging.INFO, path: str | Path | None = None +) -> logging.Logger: + """ + Create a looger with stream and optional file handlers. + + :param name: Logger name. + :param level: logging level. + :param path: Creates a file handler at the specified path if not None. + :return: Logger object. + """ + if isinstance(path, str): + path = Path(path) + + logger = logging.getLogger(name) + logger.setLevel(level) + formatter = logging.Formatter( + "%(asctime)s : %(name)s : %(levelname)s : %(message)s" + ) + + stream_handler = logging.StreamHandler() + stream_handler.setFormatter(formatter) + stream_handler.setLevel(level) + logger.addHandler(stream_handler) + + if path is not None: + filename = f"{'_'.join([k.lower() for k in name.split(' ')])}.log" + file_handler = logging.FileHandler(path / filename) + file_handler.setFormatter(formatter) + file_handler.setLevel(level) + logger.addHandler(file_handler) + + return logger def elapsed_time_logger(start, end, message): @@ -53,10 +76,11 @@ def elapsed_time_logger(start, end, message): return out -def run(filepath: str): # pylint: disable=too-many-locals +def run(filepath: Path): # pylint: disable=too-many-locals start = time() ifile = InputFile.read_ui_json(filepath) + logger = get_logger("Import Files", path=filepath.parent) logger.info( "Importing las file data to workspace %s.geoh5.", ifile.data["geoh5"].h5file.stem, @@ -65,6 +89,9 @@ def run(filepath: str): # pylint: disable=too-many-locals workspace = Workspace() begin_reading = time() + # lasfiles = [] + # for file in ifile.data["files"].split(";"): + # lasfiles.append(lasio.read(file, mnemonic_case="preserve")) with Pool() as pool: futures = [] for file in tqdm(ifile.data["files"].split(";"), desc="Reading las files"): @@ -105,9 +132,9 @@ def run(filepath: str): # pylint: disable=too-many-locals ) end = time() logger.info(elapsed_time_logger(start, end, "All done.")) - logpath = Path(file_handler.baseFilename) + logpath = Path(logger.handlers[1].baseFilename) # type: ignore dh_group.add_file(logpath) - file_handler.close() + logger.handlers[1].close() logpath.unlink() if ifile.data["monitoring_directory"]: @@ -130,4 +157,4 @@ def run(filepath: str): # pylint: disable=too-many-locals if __name__ == "__main__": - run(sys.argv[1]) + run(Path(sys.argv[1])) diff --git a/las_geoh5/import_files/params.py b/las_geoh5/import_files/params.py index eb67901..8476037 100644 --- a/las_geoh5/import_files/params.py +++ b/las_geoh5/import_files/params.py @@ -8,7 +8,6 @@ from pydantic import BaseModel, ConfigDict, model_validator LAS_GEOH5_STANDARD = { - "depth_name": "DEPTH", "collar_x_name": "X", "collar_y_name": "Y", "collar_z_name": "ELEV", @@ -19,14 +18,12 @@ class NameOptions(BaseModel): """ Stores options for naming of dillhole parameters in las files. - :param depth_name: Name of the depth field. :param collar_x_name: Name of the collar x field. :param collar_y_name: Name of the collar y field. :param collar_z_name: Name of the collar z field. """ well_name: str = "WELL" - depth_name: str = "DEPTH" collar_x_name: str = "X" collar_y_name: str = "Y" collar_z_name: str = "ELEV" diff --git a/las_geoh5/import_files/uijson.py b/las_geoh5/import_files/uijson.py index c36d864..047b37a 100644 --- a/las_geoh5/import_files/uijson.py +++ b/las_geoh5/import_files/uijson.py @@ -8,17 +8,21 @@ from copy import deepcopy -from las_geoh5.export_directories.uijson import ui_json +from geoh5py.ui_json.constants import default_ui_json + +# pylint: disable=duplicate-code ui_json = dict( - deepcopy(ui_json), + deepcopy(default_ui_json), **{ "title": "LAS files to Drillhole group", "run_command": "las_geoh5.import_files.driver", - "name": { + "conda_environment": "las-geoh5", + "drillhole_group": { "main": True, - "label": "Name", - "value": "", + "label": "Drillhole group", + "value": None, + "groupType": ["{825424fb-c2c6-4fea-9f2b-6cd00023d393}"], }, "files": { "main": True, @@ -28,44 +32,52 @@ "fileType": ["las"], "fileMulti": True, }, - "depths_name": { - "label": "Depths", - "value": "DEPTH", - "group": "Import fields", - "optional": True, - "enabled": False, + "name": { + "main": True, + "label": "Property group name", + "group": "Property group", + "value": "", + }, + "collocation_tolerance": { + "main": True, + "label": "Collocation tolerance", + "group": "Property group", + "value": 0.01, + "tooltip": ( + "Tolerance for determining collocation of data locations " + "and ultimately deciding if incoming data should belong to " + "an existing property group.", + ), }, "collar_x_name": { - "label": "Collar x", + "main": True, + "label": "Easting", + "tooltip": "Name of header field containing the collar easting.", "value": "X", - "group": "Import fields", + "group": "Collar", "optional": True, "enabled": False, }, "collar_y_name": { - "label": "Collar y", + "main": True, + "label": "Northing", + "tooltip": "Name of header field containing the collar northing.", "value": "Y", - "group": "Import fields", + "group": "Collar", "optional": True, "enabled": False, }, "collar_z_name": { - "label": "Collar z", + "main": True, + "tooltip": "Name of header field containing the collar elevation.", + "label": "Elevation", "value": "ELEV", - "group": "Import fields", + "group": "Collar", "optional": True, "enabled": False, }, - "collocation_tolerance": { - "label": "Collocation tolerance", - "value": 0.01, - "tooltip": ( - "Tolerance for determining collocation of data locations " - "and ultimately deciding if incoming data should belong to " - "an existing property group.", - ), - }, "skip_empty_header": { + "main": True, "label": "Skip empty header", "value": False, "tooltip": ( @@ -75,6 +87,7 @@ ), }, "warnings": { + "main": True, "label": "Warnings", "value": True, "tooltip": "Show warnings during import.", diff --git a/las_geoh5_assets/uijson/export_las_directories.ui.json b/las_geoh5_assets/uijson/export_las_directories.ui.json index b98c29e..796fb17 100644 --- a/las_geoh5_assets/uijson/export_las_directories.ui.json +++ b/las_geoh5_assets/uijson/export_las_directories.ui.json @@ -4,9 +4,9 @@ "run_command": "las_geoh5.export_directories.driver", "run_command_boolean": { "value": false, - "label": "Run python module", - "main": true, - "tooltip": "Warning: launches process to run python model on save" + "label": "Run python module ", + "tooltip": "Warning: launches process to run python model on save", + "main": true }, "monitoring_directory": "", "conda_environment": "las-geoh5", @@ -27,4 +27,4 @@ "optional": true, "enabled": false } -} +} \ No newline at end of file diff --git a/las_geoh5_assets/uijson/import_las_directories.ui.json b/las_geoh5_assets/uijson/import_las_directories.ui.json index 4cbde48..266aec8 100644 --- a/las_geoh5_assets/uijson/import_las_directories.ui.json +++ b/las_geoh5_assets/uijson/import_las_directories.ui.json @@ -4,9 +4,9 @@ "run_command": "las_geoh5.import_directories.driver", "run_command_boolean": { "value": false, - "label": "Run python module", - "main": true, - "tooltip": "Warning: launches process to run python model on save" + "label": "Run python module ", + "tooltip": "Warning: launches process to run python model on save", + "main": true }, "monitoring_directory": "", "conda_environment": "las-geoh5", @@ -27,4 +27,4 @@ "optional": true, "enabled": false } -} +} \ No newline at end of file diff --git a/las_geoh5_assets/uijson/import_las_files.ui.json b/las_geoh5_assets/uijson/import_las_files.ui.json index 0ed0e7a..4bc7ace 100644 --- a/las_geoh5_assets/uijson/import_las_files.ui.json +++ b/las_geoh5_assets/uijson/import_las_files.ui.json @@ -4,9 +4,9 @@ "run_command": "las_geoh5.import_files.driver", "run_command_boolean": { "value": false, - "label": "Run python module", - "main": true, - "tooltip": "Warning: launches process to run python model on save" + "label": "Run python module ", + "tooltip": "Warning: launches process to run python model on save", + "main": true }, "monitoring_directory": "", "conda_environment": "las-geoh5", @@ -20,11 +20,6 @@ "{825424fb-c2c6-4fea-9f2b-6cd00023d393}" ] }, - "name": { - "main": true, - "label": "Name", - "value": "" - }, "files": { "main": true, "label": "Files", @@ -37,47 +32,58 @@ ], "fileMulti": true }, - "depths_name": { - "label": "Depths", - "value": "DEPTH", - "group": "Import fields", - "optional": true, - "enabled": false + "name": { + "main": true, + "label": "Property group name", + "group": "Property group", + "value": "" + }, + "collocation_tolerance": { + "main": true, + "label": "Collocation tolerance", + "group": "Property group", + "value": 0.01, + "tooltip": [ + "Tolerance for determining collocation of data locations and ultimately deciding if incoming data should belong to an existing property group." + ] }, "collar_x_name": { - "label": "Collar x", + "main": true, + "label": "Easting", + "tooltip": "Name of header field containing the collar easting.", "value": "X", - "group": "Import fields", + "group": "Collar", "optional": true, "enabled": false }, "collar_y_name": { - "label": "Collar y", + "main": true, + "label": "Northing", + "tooltip": "Name of header field containing the collar northing.", "value": "Y", - "group": "Import fields", + "group": "Collar", "optional": true, "enabled": false }, "collar_z_name": { - "label": "Collar z", + "main": true, + "tooltip": "Name of header field containing the collar elevation.", + "label": "Elevation", "value": "ELEV", - "group": "Import fields", + "group": "Collar", "optional": true, "enabled": false }, - "collocation_tolerance": { - "label": "Collocation tolerance", - "value": 0.01, - "tooltip": "Tolerance for determining collocation of data locations and ultimately deciding if incoming data should belong to an existing property group." - }, "skip_empty_header": { + "main": true, "label": "Skip empty header", "value": false, "tooltip": "Importing files without collar information results in drillholes placed at the origin. Check this box to skip these files." }, "warnings": { + "main": true, "label": "Warnings", "value": true, "tooltip": "Show warnings during import." } -} +} \ No newline at end of file diff --git a/tests/import_las_test.py b/tests/import_las_test.py index ce101db..f7b92dc 100644 --- a/tests/import_las_test.py +++ b/tests/import_las_test.py @@ -10,6 +10,7 @@ import importlib import logging +from pathlib import Path import lasio import numpy as np @@ -67,7 +68,6 @@ def write_input_file( # pylint: disable=too-many-arguments drillhole_group, property_group_name, files, - depths_name, x_collar_name, y_collar_name, z_collar_name, @@ -85,7 +85,6 @@ def write_input_file( # pylint: disable=too-many-arguments "drillhole_group": drillhole_group, "name": property_group_name, "files": ";".join(files), - "depths_name": depths_name, "collar_x_name": x_collar_name, "collar_y_name": y_collar_name, "collar_z_name": z_collar_name, @@ -94,7 +93,10 @@ def write_input_file( # pylint: disable=too-many-arguments ) ifile.write_ui_json("import_las_files.ui.json", str(basepath)) - return ifile.path_name + if ifile.path_name is None: + raise ValueError("Input file path is None.") + + return Path(ifile.path_name) TEST_FILES = [ @@ -132,7 +134,6 @@ def test_import_las_new_drillholes(tmp_path): dh_group, "my_property_group", lasfiles, - "DEPTH", "UTMX", "UTMY", "ELEV", @@ -199,7 +200,6 @@ def test_import_las_existing_drillholes(tmp_path): dh_group, "my_property_group", lasfiles, - "DEPTH", "UTMX", "UTMY", "ELEV", @@ -249,7 +249,6 @@ def test_las_translator_retrieve(tmp_path): translator = LASTranslator( NameOptions( well_name="well", - depth_name="DEPTH", collar_x_name="UTMX", collar_y_name="UTMY", collar_z_name="ELEV", @@ -258,9 +257,6 @@ def test_las_translator_retrieve(tmp_path): assert translator.retrieve("collar_x_name", lasfile) == 0.0 assert translator.retrieve("collar_y_name", lasfile) == 10.0 assert translator.retrieve("well_name", lasfile) == "dh1" - assert np.allclose( - translator.retrieve("depth_name", lasfile), np.arange(0, 10, 0.5) - ) with pytest.raises( KeyError, match="'collar_z_name' field: 'ELEV' not found in las file." @@ -310,7 +306,6 @@ def test_skip_empty_header_option(tmp_path): dh_group, "my_property_group", lasfiles, - "DEPTH", "UTMX", "UTMY", "ELEV",