diff --git a/setup.cfg b/setup.cfg index 5294d93..d63384b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = hecdss -version = 0.1.26 +version = 0.1.27 author = Hydrologic Engineering Center author_email =hec.dss@usace.army.mil description = Python wrapper for the HEC-DSS file database C library. diff --git a/src/hecdss/download_hecdss.py b/src/hecdss/download_hecdss.py index 777ff22..cc3c388 100644 --- a/src/hecdss/download_hecdss.py +++ b/src/hecdss/download_hecdss.py @@ -36,7 +36,7 @@ def download_and_unzip(url, zip_file, destination_dir): print(f"Failed to download zip file. Status code: {response.status_code}") base_url = "https://www.hec.usace.army.mil/nexus/repository/maven-public/mil/army/usace/hec/hecdss/" -version = "7-IU-16" +version = "7-IW-3" destination_dir = Path(__file__).parent.joinpath("lib") zip_url = f"{base_url}{version}-win-x86_64/hecdss-{version}-win-x86_64.zip" diff --git a/src/hecdss/hecdss.py b/src/hecdss/hecdss.py index c73bdac..a3a2d7f 100644 --- a/src/hecdss/hecdss.py +++ b/src/hecdss/hecdss.py @@ -7,6 +7,7 @@ import hecdss.record_type from hecdss.array_container import ArrayContainer from hecdss.location_info import LocationInfo +from hecdss.text import Text from hecdss.paired_data import PairedData from hecdss.native import _Native from hecdss.dateconverter import DateConverter @@ -59,12 +60,18 @@ def __exit__(self, exc_type, exc_val, exc_tb): def set_global_debug_level(level: int) -> None: """ Sets the library debug level - Args: - level (int): a value between 0 and 15. Larger for more output. - For level descriptions, see zdssMessages.h of the heclib source code, - or documentation from the HEC-DSS Programmers Guide for C on the `mlvl` parameter of the `zset` utility function. + level (int): HEC-DSS message level (0-15) + 0: No messages (not recommended) + 1: Critical errors only + 2: Terse output (errors + file operations) + 3: General log messages (default) + 4: User diagnostic messages + 5: Internal debug level 1 (not recommended for users) + 6: Internal debug level 2 (full debug) + 7-15: Extended debug levels """ + # Set the native DLL level (controls what gets written) _Native().hec_dss_set_debug_level(level) def close(self): """closes the DSS file and releases any locks @@ -123,8 +130,32 @@ def get(self, pathname: str, startdatetime=None, enddatetime=None, trim=False): return self._get_array(pathname) elif type == RecordType.LocationInfo: return self._get_location_info(pathname) + elif type == RecordType.Text: + return self._get_text(pathname) return None + def _get_text(self, pathname: str): + textLength = 1024 + + + BUFFER_TOO_SMALL = -1 + textArray = [] + status = self._native.hec_dss_textRetrieve(pathname, textArray, textLength) + while status == BUFFER_TOO_SMALL: + textLength *= 2 + status = self._native.hec_dss_textRetrieve(pathname, textArray, textLength) + if textLength > 2*1048576: # 2 MB + print(f"Text record too large to read from '{pathname}'") + return None + + if status != 0: + print(f"Error reading text from '{pathname}'") + return None + text = Text() + text.id = pathname + text.text = textArray[0] + return text + def _get_array(self, pathname: str): intValuesCount = [0] floatValuesCount = [0] @@ -627,6 +658,10 @@ def put(self, container) -> int: elif type(container) is LocationInfo: status = self._native.hec_dss_locationStore(container,1) self._catalog = None + elif type(container) is Text: + text = container + status = self._native.hec_dss_textStore(text.id, text.text, len(text.text)) + self._catalog = None else: raise NotImplementedError(f"unsupported record_type: {type(container)}. Expected types are: {RecordType.SUPPORTED_RECORD_TYPES.value}") diff --git a/src/hecdss/native.py b/src/hecdss/native.py index 8968de1..bbc8149 100644 --- a/src/hecdss/native.py +++ b/src/hecdss/native.py @@ -1173,3 +1173,51 @@ def hec_dss_delete(self, pathname: str) -> int: print("Function call failed with result:", result) return result + + def hec_dss_textStore(self, pathname, text, length=None): + """ + Store text data in a DSS file. + Args: + pathname (str): The DSS pathname where the text will be stored. + text (str): The text data to store. + length (int, optional): The length of the text. If None, it will be set to the length of the text. + """ + f = self.dll.hec_dss_textStore + f.argtypes = [ + c_void_p, # dss + c_char_p, # pathname + c_char_p, # text + c_int # length + ] + f.restype = c_int + + result = f(self.handle, pathname.encode("utf-8"), + text.encode("utf-8"), + length if length is not None else len(text)) + + return result + + def hec_dss_textRetrieve(self, pathname, buffer :List[str], buff_size: int) -> int: + """ + Store text data in a DSS file. + Args: + pathname (str): The DSS pathname where the text will be stored. + text (str): The text data to store. + length (int, optional): The length of the text. If None, it will be set to the length of the text. + """ + f = self.dll.hec_dss_textRetrieve + f.argtypes = [ + c_void_p, # dss + c_char_p, # pathname + c_char_p, # buffer + c_int # buff_size + ] + f.restype = c_int + + c_buffer = create_string_buffer(buff_size) + result = f(self.handle, pathname.encode("utf-8"), + c_buffer, + buff_size) + + buffer.append(c_buffer.value.decode("utf-8")) + return result \ No newline at end of file diff --git a/src/hecdss/text.py b/src/hecdss/text.py new file mode 100644 index 0000000..f5a3150 --- /dev/null +++ b/src/hecdss/text.py @@ -0,0 +1,22 @@ + +class Text: + def __init__(self): + """ + Initialize a Text object with default values. + """ + self.id = None + self.text = "" + + @staticmethod + def create(id:str, text:str): + """ + Create a Text object with the provided text. + + Parameters: + text (str): The text data. + """ + txt = Text() + txt.id = id + txt.text = text + + return txt diff --git a/tests/test_text.py b/tests/test_text.py index a33ee73..245acc4 100644 --- a/tests/test_text.py +++ b/tests/test_text.py @@ -6,6 +6,7 @@ from hecdss import HecDss from hecdss.record_type import RecordType +from hecdss.text import Text class TestText(unittest.TestCase): @@ -16,13 +17,15 @@ def setUp(self) -> None: def tearDown(self) -> None: self.test_files.cleanup() - def test_ressim_global_variables(self): - with HecDss(self.test_files.get_copy("TestAlt1-dss-v7.dss")) as dss: - catalog = dss.get_catalog() - for ds in catalog: - print(ds.recType, ds) - self.assertEqual(RecordType.Text, ds.recType) + def test_text_from_scratch(self): + with HecDss(self.test_files.get_copy("TestAlt1-dss-v7.dss")) as dss: + path = "/A/B/C/D/E/F/" + text = "This is a test\nof text data\nin a DSS file.\n" + test_txt = Text.create(path, text) + dss.put(test_txt) + txt = dss.get(path) + self.assertEqual(test_txt.text, txt.text) if __name__ == "__main__":