ras-commander 0.42.0__py3-none-any.whl → 0.43.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,272 @@
1
+ from pathlib import Path
2
+ import h5py
3
+ import numpy as np
4
+ import pandas as pd
5
+ from geopandas import GeoDataFrame
6
+ from shapely.geometry import LineString, MultiLineString
7
+ from typing import List # Import List to avoid NameError
8
+ from .Decorators import standardize_input, log_call
9
+ from .HdfBase import HdfBase
10
+ from .HdfUtils import HdfUtils
11
+ from .LoggingConfig import get_logger
12
+
13
+ logger = get_logger(__name__)
14
+
15
+ class HdfXsec:
16
+ """
17
+ HdfXsec class for handling cross-section related operations on HEC-RAS HDF files.
18
+
19
+ This class provides methods to extract and process cross-section data, elevation information,
20
+ and river reach data from HEC-RAS HDF geometry files. It includes functionality to retrieve
21
+ cross-section attributes, elevation profiles, and river reach geometries.
22
+
23
+ The class uses static methods, allowing for direct calls without instantiation. It relies on
24
+ utility functions from HdfBase and HdfUtils classes for various operations such as projection
25
+ handling and data conversion.
26
+
27
+ Note:
28
+ This class is designed to work with HEC-RAS geometry HDF files and requires them to have
29
+ a specific structure and naming convention for the data groups and attributes.
30
+ """
31
+
32
+ @staticmethod
33
+ @log_call
34
+ @standardize_input(file_type='geom_hdf')
35
+ def cross_sections(hdf_path: Path, datetime_to_str: bool = False) -> GeoDataFrame:
36
+ """
37
+ Return the model 1D cross sections.
38
+
39
+ This method extracts cross-section data from the HEC-RAS geometry HDF file,
40
+ including attributes and geometry information.
41
+
42
+ Parameters
43
+ ----------
44
+ hdf_path : Path
45
+ Path to the HEC-RAS geometry HDF file.
46
+ datetime_to_str : bool, optional
47
+ If True, convert datetime objects to strings. Default is False.
48
+
49
+ Returns
50
+ -------
51
+ GeoDataFrame
52
+ A GeoDataFrame containing the cross sections with their attributes and geometries.
53
+
54
+ Raises
55
+ ------
56
+ KeyError
57
+ If the required datasets are not found in the HDF file.
58
+ """
59
+ try:
60
+ with h5py.File(hdf_path, 'r') as hdf_file:
61
+ xs_data = hdf_file["Geometry/Cross Sections"]
62
+
63
+ if "Attributes" not in xs_data:
64
+ logger.warning(f"No 'Attributes' dataset group in {hdf_path}")
65
+ return GeoDataFrame()
66
+
67
+ # Convert attribute values
68
+ v_conv_val = np.vectorize(HdfUtils._convert_ras_hdf_value)
69
+ xs_attrs = xs_data["Attributes"][()]
70
+ xs_dict = {"xs_id": range(xs_attrs.shape[0])}
71
+ xs_dict.update(
72
+ {name: v_conv_val(xs_attrs[name]) for name in xs_attrs.dtype.names}
73
+ )
74
+
75
+ xs_df = pd.DataFrame(xs_dict)
76
+
77
+ # Create geometry from coordinate pairs
78
+ xs_df['geometry'] = xs_df.apply(lambda row: LineString([
79
+ (row['XS_X_Coord_1'], row['XS_Y_Coord_1']),
80
+ (row['XS_X_Coord_2'], row['XS_Y_Coord_2'])
81
+ ]), axis=1)
82
+
83
+ # Convert to GeoDataFrame
84
+ gdf = GeoDataFrame(xs_df, geometry='geometry', crs=HdfUtils.projection(hdf_path))
85
+
86
+ # Convert datetime columns to strings if requested
87
+ if datetime_to_str:
88
+ gdf = HdfUtils.df_datetimes_to_str(gdf)
89
+
90
+ return gdf
91
+
92
+ except KeyError as e:
93
+ logger.error(f"Error accessing cross-section data in {hdf_path}: {str(e)}")
94
+ return GeoDataFrame()
95
+
96
+ @staticmethod
97
+ @log_call
98
+ @standardize_input(file_type='geom_hdf')
99
+ def cross_sections_elevations(hdf_path: Path, round_to: int = 2) -> pd.DataFrame:
100
+ """
101
+ Return the model cross section elevation information.
102
+
103
+ This method extracts cross-section elevation data from the HEC-RAS geometry HDF file,
104
+ including station-elevation pairs for each cross-section.
105
+
106
+ Parameters
107
+ ----------
108
+ hdf_path : Path
109
+ Path to the HEC-RAS geometry HDF file.
110
+ round_to : int, optional
111
+ Number of decimal places to round to. Default is 2.
112
+
113
+ Returns
114
+ -------
115
+ pd.DataFrame
116
+ A DataFrame containing the cross section elevation information.
117
+
118
+ Raises
119
+ ------
120
+ KeyError
121
+ If the required datasets are not found in the HDF file.
122
+ """
123
+ try:
124
+ with h5py.File(hdf_path, 'r') as hdf_file:
125
+ path = "/Geometry/Cross Sections"
126
+ if path not in hdf_file:
127
+ logger.warning(f"No 'Cross Sections' group found in {hdf_path}")
128
+ return pd.DataFrame()
129
+
130
+ xselev_data = hdf_file[path]
131
+
132
+ if "Station Elevation Info" not in xselev_data or "Station Elevation Values" not in xselev_data:
133
+ logger.warning(f"Required datasets not found in Cross Sections group in {hdf_path}")
134
+ return pd.DataFrame()
135
+
136
+ # Get cross-section data
137
+ xs_df = HdfXsec.cross_sections(hdf_path)
138
+ if xs_df.empty:
139
+ return pd.DataFrame()
140
+
141
+ # Extract elevation data
142
+ elevations = []
143
+ for part_start, part_cnt in xselev_data["Station Elevation Info"][()]:
144
+ xzdata = xselev_data["Station Elevation Values"][()][
145
+ part_start : part_start + part_cnt
146
+ ]
147
+ elevations.append(xzdata)
148
+
149
+ # Create DataFrame with elevation info
150
+ xs_elev_df = xs_df[
151
+ ["xs_id", "River", "Reach", "RS", "Left Bank", "Right Bank"]
152
+ ].copy()
153
+ xs_elev_df["Left Bank"] = xs_elev_df["Left Bank"].round(round_to).astype(str)
154
+ xs_elev_df["Right Bank"] = xs_elev_df["Right Bank"].round(round_to).astype(str)
155
+ xs_elev_df["elevation info"] = elevations
156
+
157
+ return xs_elev_df
158
+
159
+ except KeyError as e:
160
+ logger.error(f"Error accessing cross-section elevation data in {hdf_path}: {str(e)}")
161
+ return pd.DataFrame()
162
+ except Exception as e:
163
+ logger.error(f"Unexpected error in cross_sections_elevations: {str(e)}")
164
+ return pd.DataFrame()
165
+
166
+ @staticmethod
167
+ @log_call
168
+ @standardize_input(file_type='geom_hdf')
169
+ def river_reaches(hdf_path: Path, datetime_to_str: bool = False) -> GeoDataFrame:
170
+ """
171
+ Return the model 1D river reach lines.
172
+
173
+ This method extracts river reach data from the HEC-RAS geometry HDF file,
174
+ including attributes and geometry information.
175
+
176
+ Parameters
177
+ ----------
178
+ hdf_path : Path
179
+ Path to the HEC-RAS geometry HDF file.
180
+ datetime_to_str : bool, optional
181
+ If True, convert datetime objects to strings. Default is False.
182
+
183
+ Returns
184
+ -------
185
+ GeoDataFrame
186
+ A GeoDataFrame containing the river reaches with their attributes and geometries.
187
+ """
188
+ try:
189
+ with h5py.File(hdf_path, 'r') as hdf_file:
190
+ if "Geometry/River Centerlines" not in hdf_file:
191
+ return GeoDataFrame()
192
+
193
+ river_data = hdf_file["Geometry/River Centerlines"]
194
+ v_conv_val = np.vectorize(HdfUtils._convert_ras_hdf_value)
195
+ river_attrs = river_data["Attributes"][()]
196
+ river_dict = {"river_id": range(river_attrs.shape[0])}
197
+ river_dict.update(
198
+ {name: v_conv_val(river_attrs[name]) for name in river_attrs.dtype.names}
199
+ )
200
+
201
+ # Get polylines for river reaches
202
+ geoms = HdfXsec._get_polylines(hdf_path, "Geometry/River Centerlines")
203
+
204
+ river_gdf = GeoDataFrame(
205
+ river_dict,
206
+ geometry=geoms,
207
+ crs=HdfUtils.projection(hdf_path),
208
+ )
209
+ if datetime_to_str:
210
+ river_gdf["Last Edited"] = river_gdf["Last Edited"].apply(
211
+ lambda x: pd.Timestamp.isoformat(x)
212
+ )
213
+ return river_gdf
214
+ except Exception as e:
215
+ logger.error(f"Error reading river reaches: {str(e)}")
216
+ return GeoDataFrame()
217
+
218
+ @staticmethod
219
+ def _get_polylines(hdf_path: Path, path: str, info_name: str = "Polyline Info", parts_name: str = "Polyline Parts", points_name: str = "Polyline Points") -> List[LineString]:
220
+ """
221
+ Helper method to extract polylines from HDF file.
222
+
223
+ This method is used internally to extract polyline geometries for various features
224
+ such as river reaches.
225
+
226
+ Parameters
227
+ ----------
228
+ hdf_path : Path
229
+ Path to the HEC-RAS geometry HDF file.
230
+ path : str
231
+ Path within the HDF file to the polyline data.
232
+ info_name : str, optional
233
+ Name of the dataset containing polyline info. Default is "Polyline Info".
234
+ parts_name : str, optional
235
+ Name of the dataset containing polyline parts. Default is "Polyline Parts".
236
+ points_name : str, optional
237
+ Name of the dataset containing polyline points. Default is "Polyline Points".
238
+
239
+ Returns
240
+ -------
241
+ List[LineString]
242
+ A list of LineString geometries representing the polylines.
243
+ """
244
+ try:
245
+ with h5py.File(hdf_path, 'r') as hdf_file:
246
+ polyline_info_path = f"{path}/{info_name}"
247
+ polyline_parts_path = f"{path}/{parts_name}"
248
+ polyline_points_path = f"{path}/{points_name}"
249
+
250
+ polyline_info = hdf_file[polyline_info_path][()]
251
+ polyline_parts = hdf_file[polyline_parts_path][()]
252
+ polyline_points = hdf_file[polyline_points_path][()]
253
+
254
+ geoms = []
255
+ for pnt_start, pnt_cnt, part_start, part_cnt in polyline_info:
256
+ points = polyline_points[pnt_start : pnt_start + pnt_cnt]
257
+ if part_cnt == 1:
258
+ geoms.append(LineString(points))
259
+ else:
260
+ parts = polyline_parts[part_start : part_start + part_cnt]
261
+ geoms.append(
262
+ MultiLineString(
263
+ list(
264
+ points[part_pnt_start : part_pnt_start + part_pnt_cnt]
265
+ for part_pnt_start, part_pnt_cnt in parts
266
+ )
267
+ )
268
+ )
269
+ return geoms
270
+ except Exception as e:
271
+ logger.error(f"Error getting polylines: {str(e)}")
272
+ return []
ras_commander/RasCmdr.py CHANGED
@@ -47,7 +47,8 @@ from threading import Lock, Thread, current_thread
47
47
  from concurrent.futures import ThreadPoolExecutor, as_completed
48
48
  from itertools import cycle
49
49
  from typing import Union, List, Optional, Dict
50
- from ras_commander.logging_config import get_logger, log_call
50
+ from .LoggingConfig import get_logger
51
+ from .Decorators import log_call
51
52
 
52
53
  logger = get_logger(__name__)
53
54
 
@@ -36,7 +36,7 @@ import logging
36
36
  import re
37
37
  from tqdm import tqdm
38
38
  from ras_commander import get_logger
39
- from ras_commander.logging_config import log_call
39
+ from ras_commander.LoggingConfig import log_call
40
40
 
41
41
  logger = get_logger(__name__)
42
42
 
@@ -56,7 +56,7 @@ class RasExamples:
56
56
  """
57
57
  self.base_url = 'https://github.com/HydrologicEngineeringCenter/hec-downloads/releases/download/'
58
58
  self.valid_versions = [
59
- "6.5", "6.4.1", "6.3.1", "6.3", "6.2", "6.1", "6.0",
59
+ "6.6", "6.5", "6.4.1", "6.3.1", "6.3", "6.2", "6.1", "6.0",
60
60
  "5.0.7", "5.0.6", "5.0.5", "5.0.4", "5.0.3", "5.0.1", "5.0",
61
61
  "4.1", "4.0", "3.1.3", "3.1.2", "3.1.1", "3.0", "2.2"
62
62
  ]
@@ -71,6 +71,41 @@ class RasExamples:
71
71
  logger.info(f"Example projects folder: {self.projects_dir}")
72
72
  self._load_project_data()
73
73
 
74
+ @log_call
75
+ def get_example_projects(self, version_number='6.6'):
76
+ """
77
+ Download and extract HEC-RAS example projects for a specified version.
78
+ """
79
+ logger.info(f"Getting example projects for version {version_number}")
80
+ if version_number not in self.valid_versions:
81
+ error_msg = f"Invalid version number. Valid versions are: {', '.join(self.valid_versions)}"
82
+ logger.error(error_msg)
83
+ raise ValueError(error_msg)
84
+
85
+ zip_url = f"{self.base_url}1.0.33/Example_Projects_{version_number.replace('.', '_')}.zip"
86
+
87
+ self.examples_dir.mkdir(parents=True, exist_ok=True)
88
+
89
+ self.zip_file_path = self.examples_dir / f"Example_Projects_{version_number.replace('.', '_')}.zip"
90
+
91
+ if not self.zip_file_path.exists():
92
+ logger.info(f"Downloading HEC-RAS Example Projects from {zip_url}. \nThe file is over 400 MB, so it may take a few minutes to download....")
93
+ try:
94
+ response = requests.get(zip_url, stream=True)
95
+ response.raise_for_status()
96
+ with open(self.zip_file_path, 'wb') as file:
97
+ shutil.copyfileobj(response.raw, file)
98
+ logger.info(f"Downloaded to {self.zip_file_path}")
99
+ except requests.exceptions.RequestException as e:
100
+ logger.error(f"Failed to download the zip file: {e}")
101
+ raise
102
+ else:
103
+ logger.info("HEC-RAS Example Projects zip file already exists. Skipping download.")
104
+
105
+ self._load_project_data()
106
+ return self.projects_dir
107
+
108
+
74
109
  @log_call
75
110
  def _load_project_data(self):
76
111
  """
@@ -129,10 +164,10 @@ class RasExamples:
129
164
  with zipfile.ZipFile(self.zip_file_path, 'r') as zip_ref:
130
165
  for file in zip_ref.namelist():
131
166
  parts = Path(file).parts
132
- if len(parts) > 2:
167
+ if len(parts) > 1:
133
168
  folder_data.append({
134
- 'Category': parts[1],
135
- 'Project': parts[2]
169
+ 'Category': parts[0],
170
+ 'Project': parts[1]
136
171
  })
137
172
 
138
173
  self.folder_df = pd.DataFrame(folder_data).drop_duplicates()
@@ -157,39 +192,6 @@ class RasExamples:
157
192
  else:
158
193
  logger.warning("No folder data to save to CSV.")
159
194
 
160
- @log_call
161
- def get_example_projects(self, version_number='6.5'):
162
- """
163
- Download and extract HEC-RAS example projects for a specified version.
164
- """
165
- logger.info(f"Getting example projects for version {version_number}")
166
- if version_number not in self.valid_versions:
167
- error_msg = f"Invalid version number. Valid versions are: {', '.join(self.valid_versions)}"
168
- logger.error(error_msg)
169
- raise ValueError(error_msg)
170
-
171
- zip_url = f"{self.base_url}1.0.31/Example_Projects_{version_number.replace('.', '_')}.zip"
172
-
173
- self.examples_dir.mkdir(parents=True, exist_ok=True)
174
-
175
- self.zip_file_path = self.examples_dir / f"Example_Projects_{version_number.replace('.', '_')}.zip"
176
-
177
- if not self.zip_file_path.exists():
178
- logger.info(f"Downloading HEC-RAS Example Projects from {zip_url}. \nThe file is over 400 MB, so it may take a few minutes to download....")
179
- try:
180
- response = requests.get(zip_url, stream=True)
181
- response.raise_for_status()
182
- with open(self.zip_file_path, 'wb') as file:
183
- shutil.copyfileobj(response.raw, file)
184
- logger.info(f"Downloaded to {self.zip_file_path}")
185
- except requests.exceptions.RequestException as e:
186
- logger.error(f"Failed to download the zip file: {e}")
187
- raise
188
- else:
189
- logger.info("HEC-RAS Example Projects zip file already exists. Skipping download.")
190
-
191
- self._load_project_data()
192
- return self.projects_dir
193
195
 
194
196
  @log_call
195
197
  def list_categories(self):
@@ -232,12 +234,12 @@ class RasExamples:
232
234
  for project_name in project_names:
233
235
  logger.info("----- RasExamples Extracting Project -----")
234
236
  logger.info(f"Extracting project '{project_name}'")
235
- project_path = self.projects_dir / project_name
237
+ project_path = self.projects_dir
236
238
 
237
- if project_path.exists():
239
+ if (project_path / project_name).exists():
238
240
  logger.info(f"Project '{project_name}' already exists. Deleting existing folder...")
239
241
  try:
240
- shutil.rmtree(project_path)
242
+ shutil.rmtree(project_path / project_name)
241
243
  logger.info(f"Existing folder for project '{project_name}' has been deleted.")
242
244
  except Exception as e:
243
245
  logger.error(f"Failed to delete existing project folder '{project_name}': {e}")
@@ -263,9 +265,9 @@ class RasExamples:
263
265
  with zipfile.ZipFile(self.zip_file_path, 'r') as zip_ref:
264
266
  for file in zip_ref.namelist():
265
267
  parts = Path(file).parts
266
- if len(parts) > 2 and parts[2] == project_name:
267
- # Remove the first two levels (category and project name)
268
- relative_path = Path(*parts[3:])
268
+ if len(parts) > 1 and parts[1] == project_name:
269
+ # Remove the first level (category)
270
+ relative_path = Path(*parts[1:])
269
271
  extract_path = project_path / relative_path
270
272
  if file.endswith('/'):
271
273
  extract_path.mkdir(parents=True, exist_ok=True)
@@ -274,8 +276,8 @@ class RasExamples:
274
276
  with zip_ref.open(file) as source, open(extract_path, "wb") as target:
275
277
  shutil.copyfileobj(source, target)
276
278
 
277
- logger.info(f"Successfully extracted project '{project_name}' to {project_path}")
278
- extracted_paths.append(project_path)
279
+ logger.info(f"Successfully extracted project '{project_name}' to {project_path / project_name}")
280
+ extracted_paths.append(project_path / project_name)
279
281
  except zipfile.BadZipFile:
280
282
  logger.error(f"Error: The file {self.zip_file_path} is not a valid zip file.")
281
283
  except FileNotFoundError:
@@ -376,73 +378,4 @@ class RasExamples:
376
378
  raise ValueError(f"Invalid size string: {size_str}")
377
379
 
378
380
  number, unit = float(re.findall(r'[\d\.]+', size_str)[0]), re.findall(r'[BKMGT]B?', size_str)[0]
379
- return int(number * units[unit])
380
-
381
- # Example usage:
382
- # ras_examples = RasExamples()
383
- # ras_examples.download_fema_ble_models('/path/to/csv/files', '/path/to/output/folder')
384
- # extracted_paths = ras_examples.extract_project(["Bald Eagle Creek", "BaldEagleCrkMulti2D", "Muncie"])
385
- # for path in extracted_paths:
386
- # logger.info(f"Extracted to: {path}")
387
-
388
-
389
- """
390
- ### How to Use the Revised `RasExamples` Class
391
-
392
- 1. **Instantiate the Class:**
393
- ```python
394
- ras_examples = RasExamples()
395
- ```
396
-
397
- 2. **Download FEMA BLE Models:**
398
- - Ensure you have the required CSV files by visiting [FEMA's Estimated Base Flood Elevation (BFE) Viewer](https://webapps.usgs.gov/infrm/estBFE/) and using the "Download as Table" option for each BLE model you wish to access.
399
- - Call the `download_fema_ble_models` method with the appropriate paths:
400
- ```python
401
- ras_examples.download_fema_ble_models('/path/to/csv/files', '/path/to/output/folder')
402
- ```
403
- - Replace `'/path/to/csv/files'` with the directory containing your CSV files.
404
- - Replace `'/path/to/output/folder'` with the directory where you want the BLE models to be downloaded and organized.
405
-
406
- 3. **Extract Projects (If Needed):**
407
- - After downloading, you can extract specific projects using the existing `extract_project` method:
408
- ```python
409
- extracted_paths = ras_examples.extract_project(["Bald Eagle Creek", "BaldEagleCrkMulti2D", "Muncie"])
410
- for path in extracted_paths:
411
- logging.info(f"Extracted to: {path}")
412
- ```
413
-
414
- 4. **Explore Projects and Categories:**
415
- - List available categories:
416
- ```python
417
- categories = ras_examples.list_categories()
418
- ```
419
- - List projects within a specific category:
420
- ```python
421
- projects = ras_examples.list_projects(category='SomeCategory')
422
- ```
423
-
424
- 5. **Clean Projects Directory (If Needed):**
425
- - To remove all extracted projects:
426
- ```python
427
- ras_examples.clean_projects_directory()
428
- ```
429
-
430
- ### Dependencies
431
-
432
- Ensure that the following Python packages are installed:
433
-
434
- - `pandas`
435
- - `requests`
436
-
437
- You can install them using `pip`:
438
-
439
- ```bash
440
- pip install pandas requests
441
- ```
442
-
443
- ### Notes
444
-
445
- - The class uses Python's `logging` module to provide detailed information about its operations. Ensure that the logging level is set appropriately to capture the desired amount of detail.
446
- - The `download_fema_ble_models` method handles large file downloads by streaming data in chunks, which is memory-efficient.
447
- - All folder names are sanitized to prevent filesystem errors due to unsafe characters.
448
- """
381
+ return int(number * units[unit])
ras_commander/RasGeo.py CHANGED
@@ -28,8 +28,8 @@ from pathlib import Path
28
28
  from typing import List, Union
29
29
  from .RasPlan import RasPlan
30
30
  from .RasPrj import ras
31
- from ras_commander import get_logger
32
- from ras_commander.logging_config import log_call
31
+ from .LoggingConfig import get_logger
32
+ from .Decorators import log_call
33
33
 
34
34
  logger = get_logger(__name__)
35
35
 
ras_commander/RasGpt.py CHANGED
@@ -10,133 +10,10 @@ class RasGpt:
10
10
  A class containing helper functions for the RAS Commander GPT.
11
11
  """
12
12
 
13
- # READ Functions to allow GPT to read library files quickly
13
+ # to be implemented later
14
+ #
15
+ # This class will contain methods to help LLM's extract useful information from HEC-RAS models in a structured format with token budget etc.
16
+ # Templates will be used to help with this, based on the example projects (1D Steady, 1D Usteady, 1D Sediment Transport, 1D Water Quality, 2D Unsteady, 2D Steady, 2D Sediment Transport, 2D Water Quality, 2D Geospatial, 3D Unsteady, 3D Steady, 3D Sediment Transport, 3D Water Quality, 3D Geospatial).
17
+ # These will simply filter the data to only include the relevant information for the area of focus.
14
18
 
15
- @classmethod
16
- @log_call
17
- def read_library_guide(cls) -> Optional[str]:
18
- """
19
- Reads and returns the contents of the Comprehensive_Library_Guide.md file.
20
-
21
- Returns:
22
- Optional[str]: The contents of the file, or None if the file is not found.
23
- """
24
- file_path = Path(__file__).parent.parent / "docs" / "Comprehensive_Library_Guide.md"
25
- return cls._read_file(file_path)
26
-
27
-
28
- # ADD FOR read_reaadme and read_function_list
29
- # Need to add a function list separate from the Library Guide
30
-
31
- # ADD for read_example_list which will read the example folder README.ModuleNotFoundError
32
-
33
-
34
-
35
-
36
-
37
- @classmethod
38
- @log_call
39
- def read_style_guide(cls) -> Optional[str]:
40
- """
41
- Reads and returns the contents of the STYLE_GUIDE.md file.
42
-
43
- Returns:
44
- Optional[str]: The contents of the file, or None if the file is not found.
45
- """
46
- file_path = Path(__file__).parent.parent / "docs" / "STYLE_GUIDE.md"
47
- return cls._read_file(file_path)
48
-
49
-
50
- # READ CLASS FILE FUNCTIONS:
51
-
52
- @classmethod
53
- @log_call
54
- def read_class_rascmdr(cls) -> Optional[str]:
55
- """
56
- Reads and returns the contents of the RasCmdr.py file.
57
-
58
- Returns:
59
- Optional[str]: The contents of the file, or None if the file is not found.
60
- """
61
- file_path = Path(__file__).parent / "RasCmdr.py"
62
- return cls._read_file(file_path)
63
-
64
- # add one for each class file
65
-
66
-
67
-
68
-
69
-
70
- # Public Helper Functions:
71
-
72
-
73
- @classmethod
74
- @log_call
75
- def get_file_structure(cls, directory: Optional[str] = None) -> str:
76
- """
77
- Returns a string representation of the file structure of the ras_commander package.
78
-
79
- Args:
80
- directory (Optional[str]): The directory to start from. If None, uses the package root.
81
-
82
- Returns:
83
- str: A string representation of the file structure.
84
- """
85
- if directory is None:
86
- directory = Path(__file__).parent
87
-
88
- return cls._get_directory_structure(directory)
89
-
90
-
91
-
92
-
93
- # Private Helper Functions:
94
-
95
- @staticmethod
96
- def _read_file(file_path: Path) -> Optional[str]:
97
- """
98
- Helper method to read the contents of a file.
99
-
100
- Args:
101
- file_path (Path): The path to the file to be read.
102
-
103
- Returns:
104
- Optional[str]: The contents of the file, or None if the file is not found.
105
- """
106
- try:
107
- with open(file_path, 'r', encoding='utf-8') as file:
108
- return file.read()
109
- except FileNotFoundError:
110
- logger.error(f"File not found: {file_path}")
111
- return None
112
- except Exception as e:
113
- logger.error(f"Error reading file {file_path}: {str(e)}")
114
- return None
115
-
116
-
117
- @staticmethod
118
- def _get_directory_structure(directory: Path, prefix: str = "") -> str:
119
- """
120
- Helper method to recursively build the directory structure string.
121
-
122
- Args:
123
- directory (Path): The directory to process.
124
- prefix (str): The prefix to use for the current level.
125
-
126
- Returns:
127
- str: A string representation of the directory structure.
128
- """
129
- if not directory.is_dir():
130
- return ""
131
-
132
- output = []
133
- for item in sorted(directory.iterdir()):
134
- if item.name.startswith('.'):
135
- continue
136
- if item.is_dir():
137
- output.append(f"{prefix}{item.name}/")
138
- output.append(RasGpt._get_directory_structure(item, prefix + " "))
139
- else:
140
- output.append(f"{prefix}{item.name}")
141
-
142
- return "\n".join(output)
19
+ #
ras_commander/RasPlan.py CHANGED
@@ -36,8 +36,8 @@ from pathlib import Path
36
36
  from typing import Union, Any
37
37
  import logging
38
38
  import re
39
- from ras_commander import get_logger
40
- from ras_commander.logging_config import log_call
39
+ from .LoggingConfig import get_logger
40
+ from .Decorators import log_call
41
41
 
42
42
  logger = get_logger(__name__)
43
43