Skip to content

Commit fbe98e2

Browse files
authored
text data type support (#108)
* add text type with test * fix typo * update versions * cleanup
1 parent 089993b commit fbe98e2

File tree

6 files changed

+120
-12
lines changed

6 files changed

+120
-12
lines changed

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = hecdss
3-
version = 0.1.26
3+
version = 0.1.27
44
author = Hydrologic Engineering Center
55
author_email =hec.dss@usace.army.mil
66
description = Python wrapper for the HEC-DSS file database C library.

src/hecdss/download_hecdss.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def download_and_unzip(url, zip_file, destination_dir):
3636
print(f"Failed to download zip file. Status code: {response.status_code}")
3737

3838
base_url = "https://www.hec.usace.army.mil/nexus/repository/maven-public/mil/army/usace/hec/hecdss/"
39-
version = "7-IU-16"
39+
version = "7-IW-3"
4040

4141
destination_dir = Path(__file__).parent.joinpath("lib")
4242
zip_url = f"{base_url}{version}-win-x86_64/hecdss-{version}-win-x86_64.zip"

src/hecdss/hecdss.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import hecdss.record_type
88
from hecdss.array_container import ArrayContainer
99
from hecdss.location_info import LocationInfo
10+
from hecdss.text import Text
1011
from hecdss.paired_data import PairedData
1112
from hecdss.native import _Native
1213
from hecdss.dateconverter import DateConverter
@@ -59,12 +60,18 @@ def __exit__(self, exc_type, exc_val, exc_tb):
5960
def set_global_debug_level(level: int) -> None:
6061
"""
6162
Sets the library debug level
62-
6363
Args:
64-
level (int): a value between 0 and 15. Larger for more output.
65-
For level descriptions, see zdssMessages.h of the heclib source code,
66-
or documentation from the HEC-DSS Programmers Guide for C on the `mlvl` parameter of the `zset` utility function.
64+
level (int): HEC-DSS message level (0-15)
65+
0: No messages (not recommended)
66+
1: Critical errors only
67+
2: Terse output (errors + file operations)
68+
3: General log messages (default)
69+
4: User diagnostic messages
70+
5: Internal debug level 1 (not recommended for users)
71+
6: Internal debug level 2 (full debug)
72+
7-15: Extended debug levels
6773
"""
74+
# Set the native DLL level (controls what gets written)
6875
_Native().hec_dss_set_debug_level(level)
6976
def close(self):
7077
"""closes the DSS file and releases any locks
@@ -123,8 +130,32 @@ def get(self, pathname: str, startdatetime=None, enddatetime=None, trim=False):
123130
return self._get_array(pathname)
124131
elif type == RecordType.LocationInfo:
125132
return self._get_location_info(pathname)
133+
elif type == RecordType.Text:
134+
return self._get_text(pathname)
126135
return None
127136

137+
def _get_text(self, pathname: str):
138+
textLength = 1024
139+
140+
141+
BUFFER_TOO_SMALL = -1
142+
textArray = []
143+
status = self._native.hec_dss_textRetrieve(pathname, textArray, textLength)
144+
while status == BUFFER_TOO_SMALL:
145+
textLength *= 2
146+
status = self._native.hec_dss_textRetrieve(pathname, textArray, textLength)
147+
if textLength > 2*1048576: # 2 MB
148+
print(f"Text record too large to read from '{pathname}'")
149+
return None
150+
151+
if status != 0:
152+
print(f"Error reading text from '{pathname}'")
153+
return None
154+
text = Text()
155+
text.id = pathname
156+
text.text = textArray[0]
157+
return text
158+
128159
def _get_array(self, pathname: str):
129160
intValuesCount = [0]
130161
floatValuesCount = [0]
@@ -627,6 +658,10 @@ def put(self, container) -> int:
627658
elif type(container) is LocationInfo:
628659
status = self._native.hec_dss_locationStore(container,1)
629660
self._catalog = None
661+
elif type(container) is Text:
662+
text = container
663+
status = self._native.hec_dss_textStore(text.id, text.text, len(text.text))
664+
self._catalog = None
630665
else:
631666
raise NotImplementedError(f"unsupported record_type: {type(container)}. Expected types are: {RecordType.SUPPORTED_RECORD_TYPES.value}")
632667

src/hecdss/native.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,3 +1173,51 @@ def hec_dss_delete(self, pathname: str) -> int:
11731173
print("Function call failed with result:", result)
11741174

11751175
return result
1176+
1177+
def hec_dss_textStore(self, pathname, text, length=None):
1178+
"""
1179+
Store text data in a DSS file.
1180+
Args:
1181+
pathname (str): The DSS pathname where the text will be stored.
1182+
text (str): The text data to store.
1183+
length (int, optional): The length of the text. If None, it will be set to the length of the text.
1184+
"""
1185+
f = self.dll.hec_dss_textStore
1186+
f.argtypes = [
1187+
c_void_p, # dss
1188+
c_char_p, # pathname
1189+
c_char_p, # text
1190+
c_int # length
1191+
]
1192+
f.restype = c_int
1193+
1194+
result = f(self.handle, pathname.encode("utf-8"),
1195+
text.encode("utf-8"),
1196+
length if length is not None else len(text))
1197+
1198+
return result
1199+
1200+
def hec_dss_textRetrieve(self, pathname, buffer :List[str], buff_size: int) -> int:
1201+
"""
1202+
Store text data in a DSS file.
1203+
Args:
1204+
pathname (str): The DSS pathname where the text will be stored.
1205+
text (str): The text data to store.
1206+
length (int, optional): The length of the text. If None, it will be set to the length of the text.
1207+
"""
1208+
f = self.dll.hec_dss_textRetrieve
1209+
f.argtypes = [
1210+
c_void_p, # dss
1211+
c_char_p, # pathname
1212+
c_char_p, # buffer
1213+
c_int # buff_size
1214+
]
1215+
f.restype = c_int
1216+
1217+
c_buffer = create_string_buffer(buff_size)
1218+
result = f(self.handle, pathname.encode("utf-8"),
1219+
c_buffer,
1220+
buff_size)
1221+
1222+
buffer.append(c_buffer.value.decode("utf-8"))
1223+
return result

src/hecdss/text.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
class Text:
3+
def __init__(self):
4+
"""
5+
Initialize a Text object with default values.
6+
"""
7+
self.id = None
8+
self.text = ""
9+
10+
@staticmethod
11+
def create(id:str, text:str):
12+
"""
13+
Create a Text object with the provided text.
14+
15+
Parameters:
16+
text (str): The text data.
17+
"""
18+
txt = Text()
19+
txt.id = id
20+
txt.text = text
21+
22+
return txt

tests/test_text.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from hecdss import HecDss
88
from hecdss.record_type import RecordType
9+
from hecdss.text import Text
910

1011

1112
class TestText(unittest.TestCase):
@@ -16,13 +17,15 @@ def setUp(self) -> None:
1617
def tearDown(self) -> None:
1718
self.test_files.cleanup()
1819

19-
def test_ressim_global_variables(self):
20-
with HecDss(self.test_files.get_copy("TestAlt1-dss-v7.dss")) as dss:
2120

22-
catalog = dss.get_catalog()
23-
for ds in catalog:
24-
print(ds.recType, ds)
25-
self.assertEqual(RecordType.Text, ds.recType)
21+
def test_text_from_scratch(self):
22+
with HecDss(self.test_files.get_copy("TestAlt1-dss-v7.dss")) as dss:
23+
path = "/A/B/C/D/E/F/"
24+
text = "This is a test\nof text data\nin a DSS file.\n"
25+
test_txt = Text.create(path, text)
26+
dss.put(test_txt)
27+
txt = dss.get(path)
28+
self.assertEqual(test_txt.text, txt.text)
2629

2730

2831
if __name__ == "__main__":

0 commit comments

Comments
 (0)