Source code for adamspy.adripy.tool

"""Contains the DrillTool class
"""

import os
import sys
import traceback
import shutil
from thornpy import numtype
from .utilities import TO_BLOCK_HEADER_PATTERN, get_cdb_location, get_cdb_path, get_full_path
from .constants import DATABASE_INFO, TO_PARAMETER_PATTERN

[docs]class DrillTool(): """ Object representing an Adams Drill tool. Instances must be generated from a tool property file. Attributes ---------- property_file : str Tiem Orbit file representing the drill tool. name : str Name of the tool. tool_type : str Type of the tool. extension : str Extension used for tiem orbit file table : str Name of the cdb table used for the particular tool type """ _ACCEPTED_TYPES = [ 'drillpipe', 'drill_collar', 'accelerator', 'stabilizer', 'short_collar', 'dart', 'jar', 'agitator', 'blade_reamer', 'drill_n_ream', 'dog_leg_reamer', 'crossover', 'darts', 'flex_pipe', 'hw_pipe', 'pdc_bit', 'motor', 'shock_sub', 'lwd_tool', 'mfr_tool', 'mwd_tool', 'instrumentation_sub', 'generic_long', 'generic_short', 'top_drive', 'equivalent_upper_string' ] def __init__(self, property_file): """Initializes the `DrillTool` object. Parameters ---------- property_file : str Tiem Orbit file representing the drill tool. """ self._check_extension(property_file) self.property_file = get_cdb_path(property_file) self.name = self._get_name() self.tool_type = self._get_type() self.extension = self._get_extension() self.table = self._get_table() self._name_receivers = []
[docs] def bind_name_to(self, name_receiver): """Appends `name_receiver` to :attr:`_name_receivers`. All the methods in the `name_receiver` :obj:list will be called anytime the :meth:`rename()` method is called Parameters ---------- name_receiver : func Any method that takes a :class:`DrillTool`. """ self._name_receivers.append(name_receiver)
[docs] def rename(self, new_name, remove_original=False): """Renames the tool Note ---- If this tool is part of a :class:`DrillString` object, the :class:`DrillString` object will be updated with the new name and property file. Parameters ---------- new_name : str New name for the tool. remove_original : bool If `True` will rename the property file. If `False` will create a new one. """ # Determine the new filename current_filename = get_full_path(self.property_file) current_filepath = os.path.split(current_filename)[0] new_filename = os.path.join(current_filepath, f'{new_name}.{self.extension}') # Copy the file to the new location shutil.copyfile(current_filename, new_filename) self.property_file = get_cdb_path(new_filename) # Modify the property file self.modify_parameter_value('Name', new_name) # Change the name variable self.name = new_name if remove_original is True: # Delete the old property file os.remove(current_filename) # NOTE: The for loop below updates the name and property file in the DrillString object # Run all the name receivers for name_receiver in self._name_receivers: name_receiver(self)
[docs] def copy_file(self, directory=None, cdb=None): """Copies the file to `directory` or to the appropriate table in `cdb`. Parameters ---------- write_directory : str Directory in which to write the file. (default is None.) cdb : str Name of the cdb in which to write the file. This argument overrides the directory. (default is None.) Raises ------ ValueError Raised if neither directory nor cdb are given. ValueError Raised if not all parameters have been defined. Note ---- Either `directory` or `cdb` must be given. """ # Check that directory or cdb was given. if directory is None and cdb is None: raise ValueError('Either directory or cdb is required!') # Determine the filename for writing if cdb is not None: # If cdb was given filename = os.path.join(get_cdb_location(cdb), self.table, f'{self.name}.{self.extension}') else: # If cdb not given and directory was given filename = os.path.join(directory, f'{self.name}.{self.extension}') # Copy the file to the new location shutil.copyfile(get_full_path(self.property_file), filename) # Change the property_file attribute self.property_file = get_cdb_path(filename)
[docs] def get_parameter_value(self, parameter_to_get): """ Returns the value of the specified parameter from the property file. Note ---- This method cannot get parameters from the UNITS block. Parameters ---------- parameter_to_get : str Name of the parameter to get from the property file. Returns ------- :obj:`string` or :obj:`float` Value of the specified parameter from the property file. """ # Initilize return variable as None found = False # Read the property file filename = get_full_path(self.property_file) with open(filename, 'rb') as fid: lines_b = fid.readlines() # Decode each line ignore any exceptions that occur on commented lines lines = [] for line_b in lines_b: try: lines.append(line_b.decode().replace('\r', '')) except UnicodeDecodeError as err: if line_b[0:1].decode() == '$': pass else: raise err # Initialize a boolean specifying if the for loop is at a line in the [UNITS] block at_units_block = False for line in lines: # For each line in the property file if at_units_block and TO_BLOCK_HEADER_PATTERN.match(line): # If end of units block reached at_units_block = False elif line.startswith('[UNITS]'): # If beginning of units block reached at_units_block = True if TO_PARAMETER_PATTERN.match(line) and at_units_block is False: # If the line matches the pattern of a parameter definition # Split the line into parameter and value [current_parameter, current_value] = line.replace('\n','').replace(' ','').split('=') if current_parameter.lower() == parameter_to_get.lower(): # If the parameter on the current line is the parameter to get if "'" in current_value: # If the value is a string value = current_value.replace("'",'').strip() else: # If the value is a number value = float(current_value) if numtype.str_is_float(current_value) else int(current_value) found = True break if not found: # If the parameter wasn't found raise an exception filename = self.property_file raise DrillToolError(f'The parameter {parameter_to_get} was not found in {filename}!') return value
[docs] def modify_parameter_value(self, parameter_to_change, new_value): """Modifies a parameter in a :class:`DrillTool` property file. Parameters ---------- parameter_to_change : str Name of the parameter to modify. Must match the name in the property file. (Not case sensitive). new_value : :obj:`float` or :obj:`str` New value of the parameter to change new_filename : str Filename of new property file. If None the original property file will be overwritten (the default is None.) """ filename = get_full_path(self.property_file) # Read the lines in the original file into a list with open(filename, 'r') as fid: original_lines = fid.readlines() # Write a new list with the correct line modified new_lines = [] for line in original_lines: # For each line in the original file, append to new_lines new_lines.append(line) if TO_PARAMETER_PATTERN.match(line): # If the line matches the pattern of a parameter definition [current_parameter, value] = line.replace('\n','').replace(' ','').replace('\t','').split('=') if current_parameter.lower() == parameter_to_change.lower(): # If the parameter is the parameter to be changed if "'" in value: # If the value is a string, add quotes new_value = "'{}'".format(new_value) new_lines[-1] = ' {} = {}\n'.format(current_parameter, new_value) with open(filename, 'w') as fid: fid.writelines(new_lines)
def _get_name(self): return self.get_parameter_value('Name') def _get_type(self): return self.get_parameter_value('File_Type').lower() def _get_extension(self): extension = DATABASE_INFO[self.tool_type]['extension'] return extension def _get_table(self): table = DATABASE_INFO[self.tool_type]['table'] return table @classmethod def _check_extension(cls, property_file): """Checks if the extension on `property_file` is in the list of supported tool file types. Parameters ---------- property_file : str Filename of an Adams Drill property file. Raises ------ DrillToolError Raised if the extension on `property_file` is not in the list of supported tool file types. """ ext = os.path.splitext(property_file)[1] if ext not in ['.' + info['extension'] for name, info in DATABASE_INFO.items() if name in cls._ACCEPTED_TYPES]: raise DrillToolError(f'The extension {ext} is not supported by the DrillTool class.')
[docs]class DrillToolError(Exception): pass