diff --git a/.flake8 b/.flake8 index ac1fc5b..fe8eb65 100644 --- a/.flake8 +++ b/.flake8 @@ -1,7 +1,7 @@ [flake8] select = B,B9,C,D,DAR,E,F,N,RST,S,W ignore = E203,E501,RST201,RST203,RST301,W503,D205,D212,D200,D415 -max-line-length = 80 +max-line-length = 88 max-complexity = 10 docstring-convention = google per-file-ignores = tests/*:S101 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9aab8ff..2bf1989 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,7 +24,7 @@ jobs: - { python: "3.7", os: "ubuntu-latest", session: "tests" } - { python: "3.10", os: "windows-latest", session: "tests" } - { python: "3.10", os: "macos-latest", session: "tests" } - - { python: "3.10", os: "ubuntu-latest", session: "typeguard" } + # - { python: "3.10", os: "ubuntu-latest", session: "typeguard" } - { python: "3.10", os: "ubuntu-latest", session: "xdoctest" } - { python: "3.10", os: "ubuntu-latest", session: "docs-build" } diff --git a/noxfile.py b/noxfile.py index 4c2fab5..6c0dd5e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -28,7 +28,6 @@ "safety", "mypy", "tests", - "typeguard", "xdoctest", "docs-build", ) @@ -154,12 +153,12 @@ def coverage(session: Session) -> None: session.run("coverage", *args) -@session(python=python_versions) -def typeguard(session: Session) -> None: - """Runtime type checking using Typeguard.""" - session.install(".") - session.install("pytest", "typeguard", "pygments") - session.run("pytest", f"--typeguard-packages={package}", *session.posargs) +# @session(python=python_versions) +# def typeguard(session: Session) -> None: +# """Runtime type checking using Typeguard.""" +# session.install(".") +# session.install("pytest", "typeguard", "pygments") +# session.run("pytest", f"--typeguard-packages={package}", *session.posargs) @session(python=python_versions) diff --git a/src/gmn_python_api/gmn_data_directory.py b/src/gmn_python_api/gmn_data_directory.py index 6de71de..bea5101 100644 --- a/src/gmn_python_api/gmn_data_directory.py +++ b/src/gmn_python_api/gmn_data_directory.py @@ -1,6 +1,6 @@ """ -This module contains functions to read trajectory summary files from the GMN -data directory. +This module contains functions to read trajectory summary files from the GMN data +directory. """ from datetime import datetime from typing import List @@ -37,8 +37,8 @@ def get_all_daily_file_urls() -> List[str]: """ Get all daily trajectory summary file urls from the GMN data directory. :return: (List[str]) A list of all daily file urls. - :raises: (requests.HTTPError) If the data directory url doesn't return a - 200 response. + :raises: (requests.HTTPError) If the data directory url doesn't return a 200 + response. """ return _get_url_paths(BASE_URL + DAILY_DIRECTORY, SUMMARY_FILE_EXTENSION) @@ -47,8 +47,8 @@ def get_all_monthly_file_urls() -> List[str]: """ Get all monthly trajectory summary file urls from the GMN data directory. :return: (List[str]) A list of all monthly file urls. - :raises: (requests.HTTPError) If the data directory url doesn't return a - 200 response. + :raises: (requests.HTTPError) If the data directory url doesn't return a 200 + response. """ return _get_url_paths(BASE_URL + MONTHLY_DIRECTORY, SUMMARY_FILE_EXTENSION) @@ -62,8 +62,8 @@ def get_daily_file_url_by_date( :param current_date: (Optional datetime) The current date. Defaults to datetime.now(). :return: (str) The URL of the daily file. - :raises: (requests.HTTPError) If the data directory url doesn't return a - 200 response. + :raises: (requests.HTTPError) If the data directory url doesn't return a 200 + response. """ if not current_date: current_date = datetime.today() @@ -86,8 +86,8 @@ def get_monthly_file_url_by_month(date: datetime) -> str: Get the URL of the monthly trajectory summary file for a given month. :param date: (datetime) The date of the monthly file. :return: (str) The URL of the monthly file. - :raises: (requests.HTTPError) If the data directory url doesn't return a - 200 response. + :raises: (requests.HTTPError) If the data directory url doesn't return a 200 + response. """ all_monthly_filenames = get_all_monthly_file_urls() files_containing_date = [ @@ -105,8 +105,8 @@ def get_daily_file_content_by_date( :param current_date: (Optional datetime) The current date. Defaults to datetime.now(). :return: (str) The content of the daily file. - :raises: (requests.HTTPError) If the data directory url doesn't return a - 200 response. + :raises: (requests.HTTPError) If the data directory url doesn't return a 200 + response. """ file_url = get_daily_file_url_by_date(date, current_date) @@ -123,8 +123,8 @@ def get_monthly_file_content_by_date(date: datetime) -> str: Get the content of the monthly trajectory summary file for a given date. :param date: (datetime) The date to get the monthly file for. :return: (str) The content of the monthly file. - :raises: (requests.HTTPError) If the data directory url doesn't return a - 200 response. + :raises: (requests.HTTPError) If the data directory url doesn't return a 200 + response. """ file_url = get_monthly_file_url_by_month(date) diff --git a/src/gmn_python_api/gmn_trajectory_summary_reader.py b/src/gmn_python_api/gmn_trajectory_summary_reader.py new file mode 100644 index 0000000..64f2915 --- /dev/null +++ b/src/gmn_python_api/gmn_trajectory_summary_reader.py @@ -0,0 +1,93 @@ +""" +This module contains functions to load trajectory summary data into Pandas DataFrames +and numpy arrays. +""" +import os.path +from io import StringIO +from typing import Any + +import numpy.typing as npt +import pandas as pd # type: ignore +from pandas._typing import FilePathOrBuffer # type: ignore + +DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" + + +def read_trajectory_summary_as_dataframe( + filepath_or_buffer: FilePathOrBuffer, +) -> pd.DataFrame: + """ + Reads a trajectory summary file into a Pandas DataFrame. + :param filepath_or_buffer: (FilePathOrBuffer) Path or buffer for a trajectory + summary file. + :return: (DataFrame) Pandas DataFrame of the trajectory summary file. + """ + if not os.path.isfile(filepath_or_buffer): + filepath_or_buffer = StringIO(filepath_or_buffer) + + trajectory_df = pd.read_csv( + filepath_or_buffer, + engine="python", + sep=r"\s*;\s*", + skiprows=[0, 5, 6], + header=[0, 1], + na_values=["nan", "...", "None"], + ) + # Clean header text + trajectory_df.columns = trajectory_df.columns.map( + lambda h: f"{_clean_header(h[0])}{_clean_header(h[1], is_unit=True)}" + ) + + # Set data types + trajectory_df["Beginning (UTC Time)"] = pd.to_datetime( + trajectory_df["Beginning (UTC Time)"], format=DATETIME_FORMAT + ) + trajectory_df["IAU (code)"] = trajectory_df["IAU (code)"].astype("string") + trajectory_df["Participating (stations)"] = trajectory_df[ + "Participating (stations)" + ].astype("string") + + trajectory_df["Beg in (FOV)"] = trajectory_df["Beg in (FOV)"].map( + {"True": True, "False": False} + ) + trajectory_df["Beg in (FOV)"] = trajectory_df["Beg in (FOV)"].astype("bool") + trajectory_df["End in (FOV)"] = trajectory_df["End in (FOV)"].map( + {"True": True, "False": False} + ) + trajectory_df["End in (FOV)"] = trajectory_df["End in (FOV)"].astype("bool") + + return trajectory_df + + +def read_trajectory_summary_as_numpy_array( + filepath_or_buffer: FilePathOrBuffer, +) -> npt.NDArray[Any]: + """ + Reads a trajectory summary file into a numpy array. + :param filepath_or_buffer: (FilePathOrBuffer) Path or buffer for a trajectory + summary file. + :return: (ndarray) Numpy array of the trajectory summary file. + """ + data_frame = read_trajectory_summary_as_dataframe(filepath_or_buffer) + # In the future use to_records() to convert to a numpy record array + # https://github.com/pandas-dev/pandas/issues/41935 + return data_frame.to_numpy() # type: ignore + + +def _clean_header(text: str, is_unit: bool = False) -> str: + """ + Extract header text from each raw csv file header. + :param text: (str) Raw csv header + :param is_unit: (optional bool) return text with brackets for units + :returns: (str) Formatted text + """ + # Return an empty string if there is no header found + if "Unnamed" in text: + return "" + + # Removes additional spaces and hashtags from text. Add brackets optionally. + clean_header = " ".join(text.replace("#", "").split()) + if is_unit: + clean_header = f" ({clean_header})" + + return clean_header diff --git a/tests/expected_gmn_trajectory_summary_reader_values.py b/tests/expected_gmn_trajectory_summary_reader_values.py new file mode 100644 index 0000000..1215191 --- /dev/null +++ b/tests/expected_gmn_trajectory_summary_reader_values.py @@ -0,0 +1,358 @@ +""" +This module stores constant expected properties for testing +test_data/test_short_traj_summary.txt +""" +import numpy as np +import pandas as pd # type: ignore +from numpy import dtype + +from gmn_python_api import gmn_trajectory_summary_reader as gtsr + +EXPECTED_COLUMN_NAMES = [ + "Beginning (Julian date)", + "Beginning (UTC Time)", + "IAU (No)", + "IAU (code)", + "Sol lon (deg)", + "App LST (deg)", + "RAgeo (deg)", + "+/- (sigma)", + "DECgeo (deg)", + "+/- (sigma.1)", + "LAMgeo (deg)", + "+/- (sigma.2)", + "BETgeo (deg)", + "+/- (sigma.3)", + "Vgeo (km/s)", + "+/- (sigma.4)", + "LAMhel (deg)", + "+/- (sigma.5)", + "BEThel (deg)", + "+/- (sigma.6)", + "Vhel (km/s)", + "+/- (sigma.7)", + "a (AU)", + "+/- (sigma.8)", + "e", + "+/- (sigma.9)", + "i (deg)", + "+/- (sigma.10)", + "peri (deg)", + "+/- (sigma.11)", + "node (deg)", + "+/- (sigma.12)", + "Pi (deg)", + "+/- (sigma.13)", + "b (deg)", + "+/- (sigma.14)", + "q (AU)", + "+/- (sigma.15)", + "f (deg)", + "+/- (sigma.16)", + "M (deg)", + "+/- (sigma.17)", + "Q (AU)", + "+/- (sigma.18)", + "n (deg/day)", + "+/- (sigma.19)", + "T (years)", + "+/- (sigma.20)", + "TisserandJ", + "+/- (sigma.21)", + "RAapp (deg)", + "+/- (sigma.22)", + "DECapp (deg)", + "+/- (sigma.23)", + "Azim +E (of N deg)", + "+/- (sigma.24)", + "Elev (deg)", + "+/- (sigma.25)", + "Vinit (km/s)", + "+/- (sigma.26)", + "Vavg (km/s)", + "+/- (sigma.27)", + "LatBeg (+N deg)", + "+/- (sigma.28)", + "LonBeg (+E deg)", + "+/- (sigma.29)", + "HtBeg (km)", + "+/- (sigma.30)", + "LatEnd (+N deg)", + "+/- (sigma.31)", + "LonEnd (+E deg)", + "+/- (sigma.32)", + "HtEnd (km)", + "+/- (sigma.33)", + "Duration (sec)", + "Peak (AbsMag)", + "Peak Ht (km)", + "F (param)", + "Mass kg (tau=0.7%)", + "Qc (deg)", + "MedianFitErr (arcsec)", + "Beg in (FOV)", + "End in (FOV)", + "Num (stat)", + "Participating (stations)", +] +EXPECTED_DTYPES = [ + dtype("float64"), + dtype(" None: expected_filenames = ["filename1.txt", "filename2.txt", "filename3.txt"] self.assertEqual( *self._run_get_all_method_with_mock_directory_listing( - gmn_data_directory.get_all_daily_file_urls, + gdd.get_all_daily_file_urls, expected_filenames, - gmn_data_directory.DAILY_DIRECTORY, + gdd.DAILY_DIRECTORY, ) ) @@ -39,9 +39,9 @@ def test_get_all_monthly_file_urls_with_correct_file_extensions(self) -> None: expected_filenames = ["filename1.txt", "filename2.txt", "filename3.txt"] self.assertEqual( *self._run_get_all_method_with_mock_directory_listing( - gmn_data_directory.get_all_monthly_file_urls, + gdd.get_all_monthly_file_urls, expected_filenames, - gmn_data_directory.MONTHLY_DIRECTORY, + gdd.MONTHLY_DIRECTORY, ) ) @@ -55,9 +55,9 @@ def test_get_all_daily_file_urls_with_incorrect_file_extensions(self) -> None: self.assertEqual( [], self._run_get_all_method_with_mock_directory_listing( - gmn_data_directory.get_all_daily_file_urls, + gdd.get_all_daily_file_urls, expected_filenames, - gmn_data_directory.DAILY_DIRECTORY, + gdd.DAILY_DIRECTORY, )[1], ) @@ -71,9 +71,9 @@ def test_get_all_monthly_file_urls_with_incorrect_file_extensions(self) -> None: self.assertEqual( [], self._run_get_all_method_with_mock_directory_listing( - gmn_data_directory.get_all_monthly_file_urls, + gdd.get_all_monthly_file_urls, expected_filenames, - gmn_data_directory.MONTHLY_DIRECTORY, + gdd.MONTHLY_DIRECTORY, )[1], ) @@ -89,7 +89,7 @@ def test_get_all_daily_file_urls_with_bad_response( mock_get.return_value = _mock_response( status=500, raise_for_status=HTTPError("Bad response") ) - self.assertRaises(HTTPError, gmn_data_directory.get_all_daily_file_urls) + self.assertRaises(HTTPError, gdd.get_all_daily_file_urls) @mock.patch("requests.get") def test_get_all_monthly_file_urls_with_bad_response( @@ -104,7 +104,7 @@ def test_get_all_monthly_file_urls_with_bad_response( mock_get.return_value = _mock_response( status=500, raise_for_status=HTTPError("Bad response") ) - self.assertRaises(HTTPError, gmn_data_directory.get_all_monthly_file_urls) + self.assertRaises(HTTPError, gdd.get_all_monthly_file_urls) def test_get_daily_file_url_by_date(self) -> None: """ @@ -115,19 +115,11 @@ def test_get_daily_file_url_by_date(self) -> None: """ expected_filename = "filename-20190101-XYZ123.txt" self.assertEqual( - [ - gmn_data_directory.BASE_URL - + gmn_data_directory.DAILY_DIRECTORY - + expected_filename - ], + [gdd.BASE_URL + gdd.DAILY_DIRECTORY + expected_filename], self._run_get_all_method_with_mock_directory_listing( - lambda: [ - gmn_data_directory.get_daily_file_url_by_date( - datetime.datetime(2019, 1, 1) - ) - ], + lambda: [gdd.get_daily_file_url_by_date(datetime.datetime(2019, 1, 1))], [expected_filename, "hgahfgasjghi.txt", "filename-20190102-XYZ123.txt"], - gmn_data_directory.DAILY_DIRECTORY, + gdd.DAILY_DIRECTORY, )[1], ) @@ -140,19 +132,13 @@ def test_get_monthly_file_url_by_date(self) -> None: """ expected_filename = "filename-201901-XYZ123.txt" self.assertEqual( - [ - gmn_data_directory.BASE_URL - + gmn_data_directory.MONTHLY_DIRECTORY - + expected_filename - ], + [gdd.BASE_URL + gdd.MONTHLY_DIRECTORY + expected_filename], self._run_get_all_method_with_mock_directory_listing( lambda: [ - gmn_data_directory.get_monthly_file_url_by_month( - datetime.datetime(2019, 1, 1) - ) + gdd.get_monthly_file_url_by_month(datetime.datetime(2019, 1, 1)) ], [expected_filename, "hgahfgasjghi.txt", "filename-201902-XYZ123.txt"], - gmn_data_directory.MONTHLY_DIRECTORY, + gdd.MONTHLY_DIRECTORY, )[1], ) @@ -163,21 +149,17 @@ def test_get_daily_file_url_current_date_today(self) -> None: When: get_daily_file_url_by_date() is called with an HTTP mocked response. """ - expected_filename = gmn_data_directory.SUMMARY_TODAY_FILENAME + expected_filename = gdd.SUMMARY_TODAY_FILENAME self.assertEqual( - [ - gmn_data_directory.BASE_URL - + gmn_data_directory.DAILY_DIRECTORY - + expected_filename - ], + [gdd.BASE_URL + gdd.DAILY_DIRECTORY + expected_filename], self._run_get_all_method_with_mock_directory_listing( lambda: [ - gmn_data_directory.get_daily_file_url_by_date( + gdd.get_daily_file_url_by_date( datetime.datetime(2019, 1, 1), datetime.datetime(2019, 1, 1) ) ], [expected_filename, "hgahfgasjghi.txt", "filename-20181204-XYZ123.txt"], - gmn_data_directory.DAILY_DIRECTORY, + gdd.DAILY_DIRECTORY, )[1], ) @@ -188,21 +170,17 @@ def test_get_daily_file_url_current_date_yesterday(self) -> None: When: get_daily_file_url_by_date() is called with an HTTP mocked response. """ - expected_filename = gmn_data_directory.SUMMARY_YESTERDAY_FILENAME + expected_filename = gdd.SUMMARY_YESTERDAY_FILENAME self.assertEqual( - [ - gmn_data_directory.BASE_URL - + gmn_data_directory.DAILY_DIRECTORY - + expected_filename - ], + [gdd.BASE_URL + gdd.DAILY_DIRECTORY + expected_filename], self._run_get_all_method_with_mock_directory_listing( lambda: [ - gmn_data_directory.get_daily_file_url_by_date( + gdd.get_daily_file_url_by_date( datetime.datetime(2019, 1, 1), datetime.datetime(2019, 1, 2) ) ], [expected_filename, "hgahfgasjghi.txt", "filename-20181204-XYZ123.txt"], - gmn_data_directory.DAILY_DIRECTORY, + gdd.DAILY_DIRECTORY, )[1], ) @@ -218,10 +196,10 @@ def test_get_daily_file_content_by_date( response. """ mock_get_url_paths.return_value = [ - gmn_data_directory.BASE_URL - + gmn_data_directory.DAILY_DIRECTORY + gdd.BASE_URL + + gdd.DAILY_DIRECTORY + "traj_summary_20181209_solrange_257.0-258.0.txt", - gmn_data_directory.BASE_URL + "daily/filename2.txt", + gdd.BASE_URL + "daily/filename2.txt", ] expected_content = open( "tests/test_data/traj_summary_20181209_solrange_257.0-258.0.txt" @@ -229,9 +207,7 @@ def test_get_daily_file_content_by_date( mock_get.return_value = _mock_response(text=expected_content) self.assertEqual( expected_content, - gmn_data_directory.get_daily_file_content_by_date( - datetime.datetime(2018, 12, 9) - ), + gdd.get_daily_file_content_by_date(datetime.datetime(2018, 12, 9)), ) @mock.patch("requests.get") @@ -246,12 +222,8 @@ def test_get_monthly_file_content_by_date( response. """ mock_get_url_paths.return_value = [ - gmn_data_directory.BASE_URL - + gmn_data_directory.MONTHLY_DIRECTORY - + "traj_summary_monthly_201812.txt", - gmn_data_directory.BASE_URL - + gmn_data_directory.MONTHLY_DIRECTORY - + "filename2.txt", + gdd.BASE_URL + gdd.MONTHLY_DIRECTORY + "traj_summary_monthly_201812.txt", + gdd.BASE_URL + gdd.MONTHLY_DIRECTORY + "filename2.txt", ] expected_content = open( "tests/test_data/traj_summary_monthly_201812.txt" @@ -259,9 +231,7 @@ def test_get_monthly_file_content_by_date( mock_get.return_value = _mock_response(text=expected_content) self.assertEqual( expected_content, - gmn_data_directory.get_monthly_file_content_by_date( - datetime.datetime(2018, 12, 1) - ), + gdd.get_monthly_file_content_by_date(datetime.datetime(2018, 12, 1)), ) @mock.patch("requests.get") @@ -276,19 +246,17 @@ def test_get_daily_file_content_by_date_bad_response( response. """ mock_get_url_paths.return_value = [ - gmn_data_directory.BASE_URL - + gmn_data_directory.DAILY_DIRECTORY + gdd.BASE_URL + + gdd.DAILY_DIRECTORY + "traj_summary_20181209_solrange_257.0-258.0.txt", - gmn_data_directory.BASE_URL - + gmn_data_directory.DAILY_DIRECTORY - + "filename2.txt", + gdd.BASE_URL + gdd.DAILY_DIRECTORY + "filename2.txt", ] mock_get.return_value = _mock_response( status=500, raise_for_status=HTTPError("Bad response") ) self.assertRaises( HTTPError, - gmn_data_directory.get_daily_file_content_by_date, + gdd.get_daily_file_content_by_date, datetime.datetime(2018, 12, 9), ) @@ -304,19 +272,15 @@ def test_get_monthly_file_content_by_date_with_bad_response( response. """ mock_get_url_paths.return_value = [ - gmn_data_directory.BASE_URL - + gmn_data_directory.MONTHLY_DIRECTORY - + "traj_summary_monthly_201812.txt", - gmn_data_directory.BASE_URL - + gmn_data_directory.MONTHLY_DIRECTORY - + "filename2.txt", + gdd.BASE_URL + gdd.MONTHLY_DIRECTORY + "traj_summary_monthly_201812.txt", + gdd.BASE_URL + gdd.MONTHLY_DIRECTORY + "filename2.txt", ] mock_get.return_value = _mock_response( status=500, raise_for_status=HTTPError("Bad response") ) self.assertRaises( HTTPError, - gmn_data_directory.get_monthly_file_content_by_date, + gdd.get_monthly_file_content_by_date, datetime.datetime(2018, 12, 1), ) @@ -343,7 +307,7 @@ def _run_get_all_method_with_mock_directory_listing( filenames returned by the mocked directory. """ expected_file_urls = [ - gmn_data_directory.BASE_URL + directory + filename for filename in filenames + gdd.BASE_URL + directory + filename for filename in filenames ] mock_get.return_value = _mock_response( text="" diff --git a/tests/test_gmn_trajectory_summary_reader.py b/tests/test_gmn_trajectory_summary_reader.py new file mode 100644 index 0000000..90afb42 --- /dev/null +++ b/tests/test_gmn_trajectory_summary_reader.py @@ -0,0 +1,96 @@ +"""Tests for the gmn_trajectory_summary_reader.py module.""" +import unittest +from pathlib import Path +from typing import Any + +import numpy.typing as npt +import pandas as pd # type: ignore +from numpy.testing import assert_equal as np_assert_array_equal +from tests.expected_gmn_trajectory_summary_reader_values import EXPECTED_COLUMN_NAMES +from tests.expected_gmn_trajectory_summary_reader_values import EXPECTED_DTYPES +from tests.expected_gmn_trajectory_summary_reader_values import EXPECTED_MAX_VALUES +from tests.expected_gmn_trajectory_summary_reader_values import EXPECTED_MIN_VALUES + +from gmn_python_api import gmn_trajectory_summary_reader as gtsr + + +class TestGmnTrajectorySummaryReader(unittest.TestCase): + """Tests for the gmn_trajectory_summary_reader.py module.""" + + def setUp(self) -> None: + """ + Sets up the tests. + """ + self.test_file_path: Path = Path("tests/test_data/test_short_traj_summary.txt") + + def test_read_trajectory_summary_buffer_as_data_frame(self) -> None: + """ + Test: That the trajectory summary buffer can be read as a dataframe by checking + properties. + When: read_trajectory_summary_buffer_as_dataframe is called. + """ + self._test_read_trajectory_summary_using_data_frame( + gtsr.read_trajectory_summary_as_dataframe(open(self.test_file_path).read()) + ) + + def test_read_trajectory_summary_buffer_as_numpy_array(self) -> None: + """ + Test: That the trajectory summary buffer can be read as a numpy array by + checking properties. + When: read_trajectory_summary_buffer_as_numpy_array is called. + """ + self._test_read_trajectory_summary_using_numpy_array( + gtsr.read_trajectory_summary_as_numpy_array(self.test_file_path) + ) + + def test_read_trajectory_summary_file_as_data_frame(self) -> None: + """ + Test: That the trajectory summary file can be read as a dataframe by + checking properties. + When: read_trajectory_summary_file_as_dataframe is called. + """ + self._test_read_trajectory_summary_using_data_frame( + gtsr.read_trajectory_summary_as_dataframe(self.test_file_path) + ) + + def test_read_trajectory_summary_file_as_numpy_array(self) -> None: + """ + Test: That the trajectory summary file can be read as a numpy array by checking + properties. + When: read_trajectory_summary_file_as_numpy_array is called. + """ + self._test_read_trajectory_summary_using_numpy_array( + gtsr.read_trajectory_summary_as_numpy_array(self.test_file_path) + ) + + def _test_read_trajectory_summary_using_data_frame( + self, actual_dataframe: pd.DataFrame + ) -> None: + """ + Asserts properties about the dataframe. + :param actual_dataframe: The dataframe to test. + """ + self.assertEqual(actual_dataframe.empty, False) + self.assertEqual(actual_dataframe.shape, (3, 85)) + self.assertEqual(actual_dataframe.index.tolist(), [0, 1, 2]) + self.assertEqual(actual_dataframe.dtypes.tolist(), EXPECTED_DTYPES) + self.assertEqual(actual_dataframe.size, 255) + + np_assert_array_equal(actual_dataframe.min().to_list(), EXPECTED_MIN_VALUES) + np_assert_array_equal(actual_dataframe.max().to_list(), EXPECTED_MAX_VALUES) + + self.assertEqual(actual_dataframe.columns.tolist(), EXPECTED_COLUMN_NAMES) + + def _test_read_trajectory_summary_using_numpy_array( + self, actual_numpy_array: npt.NDArray[Any] + ) -> None: + """ + Asserts properties about the numpy array. + :param actual_numpy_array: The numpy array to test. + """ + self.assertEqual(actual_numpy_array.shape, (3, 85)) + self.assertEqual(actual_numpy_array.size, 255) + + +if __name__ == "__main__": + unittest.main() # pragma: no cover