Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read Directly From Cloud Functionality #104

Closed
4 changes: 2 additions & 2 deletions echoregions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .core import read_evl, read_evr
from .core import read_cloud_evl, read_cloud_evr, read_evl, read_evr

__all__ = ["read_evl", "read_evr"]
__all__ = ["read_evl", "read_evr", "read_cloud_evr", "read_cloud_evl"]
199 changes: 199 additions & 0 deletions echoregions/core.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import os
from typing import Union

import s3fs

from .lines.lines import Lines
from .regions2d.regions2d import Regions2D

Expand Down Expand Up @@ -40,3 +45,197 @@
Object that contains EVL data and metadata with methods for saving to file.
"""
return Lines(input_file=str(filepath), nan_depth_value=nan_depth_value)


def read_cloud_evr(
s3_path: str,
s3_key: str,
s3_secret: str,
target_directory_path: str,
min_depth: float = None,
max_depth: float = None,
) -> Regions2D:
"""Read an EVR file from the cloud into a Regions2D object.

Parameters
----------
s3_path : str
A valid path to either a evr file on the cloud.
s3_key: str
Valid S3 Bucket Key.
s3_secret: str
Valid S3 Bucket Secret.
target_directory_path: str
Valid relative directory to temporarily place cloud file. Defaults to the
/echoregions/tmp directory. Must be a non-existent directory.
min_depth : float, default ``None``
Depth value in meters to set -9999.99 depth edges to.
max_depth : float, default ``None``
Depth value in meters to set 9999.99 depth edges to.

Returns
-------
Regions2d
Object that contains the either evr data and metadata
with methods for saving to file.
"""

return read_cloud(

Check warning on line 83 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L83

Added line #L83 was not covered by tests
file_type="evr",
s3_path=s3_path,
s3_key=s3_key,
s3_secret=s3_secret,
target_directory_path=target_directory_path,
min_depth=min_depth,
max_depth=max_depth,
)


def read_cloud_evl(
s3_path: str,
s3_key: str,
s3_secret: str,
target_directory_path: str,
nan_depth_value: float = None,
) -> Regions2D:
"""Read an EVR file from the cloud into a Regions2D object.

Parameters
----------
s3_path : str
A valid path to either a evr file on the cloud.
s3_key: str
Valid S3 Bucket Key.
s3_secret: str
Valid S3 Bucket Secret.
target_directory_path: str
Valid relative directory to temporarily place cloud file. Defaults to the
/echoregions/tmp directory. Must be a non-existent directory.
nan_depth_value : float, default ``None``
Depth in meters to replace -10000.990000 ranges with.

Returns
-------
Regions2d
Object that contains the either evr data and metadata
with methods for saving to file.
"""

return read_cloud(

Check warning on line 124 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L124

Added line #L124 was not covered by tests
file_type="evl",
s3_path=s3_path,
s3_key=s3_key,
s3_secret=s3_secret,
target_directory_path=target_directory_path,
nan_depth_value=nan_depth_value,
)


def read_cloud(
file_type: str,
s3_path: str,
s3_key: str,
s3_secret: str,
target_directory_path: str = os.getcwd() + "/echoregions/tmp/",
min_depth: float = None,
max_depth: float = None,
nan_depth_value: float = None,
) -> Union["Regions2D", "Lines"]:
"""Read an EVR file from the cloud into a Regions2D object.

Parameters
----------
file_type: str

s3_path : str
A valid path to either a evr or evl file on the cloud.
s3_key: str
Valid S3 Bucket Key.
s3_secret: str
Valid S3 Bucket Secret.
target_directory_path: str
Valid relative directory to temporarily place cloud file. Defaults to the
/echoregions/tmp directory. Must be a non-existent directory.
min_depth : float, default ``None``
Depth value in meters to set -9999.99 depth edges to.
max_depth : float, default ``None``
Depth value in meters to set 9999.99 depth edges to.
nan_depth_value : float, default ``None``
Depth in meters to replace -10000.990000 ranges with.

Returns
-------
Regions2D, Lines
Object that contains the either evr or evl data and metadata
with methods for saving to file.
"""
# Check file type. Must be evr or evl.
if file_type not in ["evr", "evl"]:
raise ValueError(f"file_type is {file_type}. Must be evl or evr. ")

Check warning on line 174 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L173-L174

Added lines #L173 - L174 were not covered by tests

# Ensure correct variables are being passed in.
if file_type == "evl" and (min_depth is not None or max_depth is not None):
raise ValueError(

Check warning on line 178 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L177-L178

Added lines #L177 - L178 were not covered by tests
"file_type evl does not use min_depth or max_depth values. \
Please clear input for mentioned variables."
)
elif file_type == "evr" and nan_depth_value is not None:
raise ValueError(

Check warning on line 183 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L182-L183

Added lines #L182 - L183 were not covered by tests
"file_type evr does not use nan_depth_values. \
Please clear input for nan_depth_values."
)

if isinstance(s3_key, str) and isinstance(s3_secret, str):
try:

Check warning on line 189 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L188-L189

Added lines #L188 - L189 were not covered by tests
# Get access to S3 bucket filesystem.
fs = s3fs.S3FileSystem(

Check warning on line 191 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L191

Added line #L191 was not covered by tests
key=s3_key,
secret=s3_secret,
)
except Exception as e:
print(e)

Check warning on line 196 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L195-L196

Added lines #L195 - L196 were not covered by tests

# Create directory if not exists. Else, throw value error.
if not os.path.exists(target_directory_path):
os.makedirs(target_directory_path)

Check warning on line 200 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L199-L200

Added lines #L199 - L200 were not covered by tests
else:
raise ValueError(

Check warning on line 202 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L202

Added line #L202 was not covered by tests
f"Directory {target_directory_path} already exists. Please \
choose a path for a directory that does not current exist."
)

# Download File
try:
fs.download(s3_path, target_directory_path)

Check warning on line 209 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L208-L209

Added lines #L208 - L209 were not covered by tests

# Check which file it is in.
file_name = os.listdir(target_directory_path)[

Check warning on line 212 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L212

Added line #L212 was not covered by tests
0
] # Should be only file in directory.
target_path = target_directory_path + "/" + file_name

Check warning on line 215 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L215

Added line #L215 was not covered by tests

# Check if filetype is evr or evl and create object based off of filetype.
if file_type == "evr":
from echoregions import read_evr

Check warning on line 219 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L218-L219

Added lines #L218 - L219 were not covered by tests

r2d = read_evr(

Check warning on line 221 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L221

Added line #L221 was not covered by tests
filepath=target_path, min_depth=min_depth, max_depth=max_depth
)
return_object = r2d

Check warning on line 224 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L224

Added line #L224 was not covered by tests
else:
from echoregions import read_evl

Check warning on line 226 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L226

Added line #L226 was not covered by tests

lines = read_evl(filepath=target_path, nan_depth_value=nan_depth_value)
return_object = lines

Check warning on line 229 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L228-L229

Added lines #L228 - L229 were not covered by tests

# Remove target path and target_directory path.
os.remove(target_path)
os.removedirs(target_directory_path)

Check warning on line 233 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L232-L233

Added lines #L232 - L233 were not covered by tests

return return_object
except Exception as e:

Check warning on line 236 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L235-L236

Added lines #L235 - L236 were not covered by tests
# Remove target directory created prior to download attempt.
os.removedirs(target_directory_path)
print(e)

Check warning on line 239 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L238-L239

Added lines #L238 - L239 were not covered by tests
else:
raise TypeError("Both s3_key and s3 secret must be of type str.")

Check warning on line 241 in echoregions/core.py

View check run for this annotation

Codecov / codecov/patch

echoregions/core.py#L241

Added line #L241 was not covered by tests
6 changes: 3 additions & 3 deletions echoregions/lines/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pandas import DataFrame, Timestamp
from xarray import DataArray

from ..utils.io import validate_path
from ..utils.io import validate_save_path
from .lines_parser import parse_line_file

ECHOVIEW_NAN_DEPTH_VALUE = -10000.99
Expand Down Expand Up @@ -63,7 +63,7 @@ def to_csv(self, save_path: bool = None) -> None:
path to save the CSV file to
"""
# Check if the save directory is safe
save_path = validate_path(
save_path = validate_save_path(
save_path=save_path, input_file=self.input_file, ext=".csv"
)
# Reorder columns and export to csv
Expand All @@ -84,7 +84,7 @@ def to_json(self, save_path: str = None, pretty: bool = True, **kwargs) -> None:
keyword arguments passed into `parse_file`
"""
# Check if the save directory is safe
save_path = validate_path(
save_path = validate_save_path(
save_path=save_path, input_file=self.input_file, ext=".json"
)
indent = 4 if pretty else None
Expand Down
4 changes: 2 additions & 2 deletions echoregions/lines/lines_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import pandas as pd

from ..utils.io import check_file
from ..utils.io import check_file_extension_existence
from ..utils.time import parse_time


def parse_line_file(input_file: str):
# Check for validity of input_file
check_file(input_file, "EVL")
check_file_extension_existence(input_file, "EVL")
# Read file and read all lines
fid = open(input_file, encoding="utf-8-sig")
file_lines = fid.readlines()
Expand Down
4 changes: 2 additions & 2 deletions echoregions/regions2d/regions2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pandas import DataFrame, Series, Timestamp
from xarray import DataArray

from ..utils.io import validate_path
from ..utils.io import validate_save_path
from ..utils.time import parse_simrad_fname_time
from .regions2d_parser import parse_regions_file

Expand Down Expand Up @@ -53,7 +53,7 @@ def to_csv(self, save_path: bool = None) -> None:
path to save the CSV file to
"""
# Check if the save directory is safe
save_path = validate_path(
save_path = validate_save_path(
save_path=save_path, input_file=self.input_file, ext=".csv"
)
# Reorder columns and export to csv
Expand Down
4 changes: 2 additions & 2 deletions echoregions/regions2d/regions2d_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import pandas as pd
from numpy import ndarray

from ..utils.io import check_file
from ..utils.io import check_file_extension_existence
from ..utils.time import parse_time


def parse_regions_file(input_file: str):
# Check for validity of input_file.
check_file(input_file, "EVR")
check_file_extension_existence(input_file, "EVR")

# Read file.
fid = open(input_file, encoding="utf-8-sig")
Expand Down
10 changes: 8 additions & 2 deletions echoregions/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from .io import check_file, from_JSON
from .io import check_file_extension_existence, from_JSON, validate_save_path
from .time import parse_simrad_fname_time, parse_time

__all__ = ["from_JSON", "check_file", "parse_simrad_fname_time", "parse_time"]
__all__ = [
"from_JSON",
"check_file_extension_existence",
"validate_save_path",
"parse_simrad_fname_time",
"parse_time",
]
31 changes: 15 additions & 16 deletions echoregions/utils/io.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import json
import os
import re
from pathlib import Path
from typing import Dict, List, Union
from typing import Dict


def from_JSON(j: str) -> Dict:
Expand All @@ -23,7 +24,7 @@
return data_dict


def validate_path(
def validate_save_path(
save_path: str = None, input_file: str = None, ext: str = ".json"
) -> str:
# Check if save_path is specified.
Expand Down Expand Up @@ -59,20 +60,18 @@
return str(save_path)


def check_file(file: str, format: Union[List[str], str]) -> None:
def check_file_extension_existence(file: str, format: str) -> None:
"""
Checks if file extension is correct and if file exists.

Arguments:
file: str; filename for file to be checked.
format: str; desired value for file extension of input file.
"""
if file is not None:
if isinstance(format, List):
within = False
for str_value in format:
if file.upper().endswith(str_value):
within = True
if not within:
raise ValueError(f"Input file {file} is not a {format} file")
else:
if not file.upper().endswith(format):
raise ValueError(f"Input file {file} is not a {format} file")
if not re.search(rf".{format}$", file, flags=re.IGNORECASE):
raise ValueError(f"Input file {file} is not a {format} file")

Check warning on line 73 in echoregions/utils/io.py

View check run for this annotation

Codecov / codecov/patch

echoregions/utils/io.py#L73

Added line #L73 was not covered by tests
if not os.path.isfile(file):
if not os.path.isdir(file):
raise ValueError(f"{file} does not exist as file or directory.")
raise ValueError(f"{file} does not exist as file.")

Check warning on line 75 in echoregions/utils/io.py

View check run for this annotation

Codecov / codecov/patch

echoregions/utils/io.py#L75

Added line #L75 was not covered by tests
else:
raise TypeError("Input file must not be of type None")
raise TypeError("Input file must not be None")

Check warning on line 77 in echoregions/utils/io.py

View check run for this annotation

Codecov / codecov/patch

echoregions/utils/io.py#L77

Added line #L77 was not covered by tests
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pre-commit
pytest
xarray>=2023.2.0
pytest-cov
python-dotenv
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ shapely>=2.0.0
zarr
netcdf4
scipy
s3fs