diff --git a/src/aind_data_schema_models/data_name_patterns.py b/src/aind_data_schema_models/data_name_patterns.py index c650105..2cff759 100644 --- a/src/aind_data_schema_models/data_name_patterns.py +++ b/src/aind_data_schema_models/data_name_patterns.py @@ -50,18 +50,57 @@ class Group(str, Enum): def datetime_to_name_string(dt: datetime) -> str: - """Take a date and time object, format it as a string""" + """ + Take a datetime object, format it as a string + Parameters + ---------- + dt : datetime + For example, datetime(2020, 12, 29, 10, 04, 59) + + Returns + ------- + str + For example, '2020-12-29_10-04-59' + + """ return dt.strftime("%Y-%m-%d_%H-%M-%S") def datetime_from_name_string(d: str, t: str) -> datetime: - """Take date and time strings, generate date and time objects""" + """ + Take date and time strings, generate datetime object + Parameters + ---------- + d : str + Date string formatted as %Y-%m-%d + t : str + Time string formatted as %H-%M-%S + + Returns + ------- + datetime + + """ d = datetime.strptime(d, "%Y-%m-%d").date() t = datetime.strptime(t, "%H-%M-%S").time() return datetime.combine(d, t) def build_data_name(label: str, creation_datetime: datetime) -> str: - """Construct a valid data description name""" + """ + Construct a data description name from a label and datetime object + Parameters + ---------- + label : str + For example, 'ecephys_123456' + creation_datetime : datetime + For example, datetime(2020, 12, 29, 10, 04, 59) + + Returns + ------- + str + For example, 'ecephys_123456_2020-12-29_10-04-59' + + """ dt_str = datetime_to_name_string(creation_datetime) return f"{label}_{dt_str}" diff --git a/tests/test_data_name_patterns.py b/tests/test_data_name_patterns.py index 44ea190..0d6893d 100644 --- a/tests/test_data_name_patterns.py +++ b/tests/test_data_name_patterns.py @@ -1,15 +1,14 @@ """Tests classes in data_name_patterns module""" import unittest +from datetime import datetime, timezone from aind_data_schema_models.data_name_patterns import ( - datetime_to_name_string, - datetime_from_name_string, - build_data_name, - RegexParts, DataRegex, - DataLevel, - Group, + RegexParts, + build_data_name, + datetime_from_name_string, + datetime_to_name_string, ) @@ -28,11 +27,89 @@ def test_patterns_success(self): def test_patterns_fail(self): """Tests that the regex patterns match unsuccessfully.""" - deformed_date = "10/19/2020" - deformed_time = "8:30:59" + malformed_date = "10/19/2020" + malformed_time = "8:30:59" + + self.assertNotRegex(malformed_date, RegexParts.DATE) + self.assertNotRegex(malformed_time, RegexParts.TIME) + + +class TestDataRegex(unittest.TestCase): + """Tests methods in DataRegex class""" + + def test_patterns_success(self): + """Tests that the regex patterns match successfully.""" + + data = "ecephys_2020-10-19_08-30-59" + raw = "ecephys_123455_2020-10-19_08-30-59" + derived = "ecephys_123455_2020-10-19_08-30-59_sorted_2020-11-21_09-31-58" + analyzed = "ecephys_123455_2020-10-19_08-30-59_sorted_2020-11-21_09-31-58" + no_underscores = "abc-123" + no_special_chars = "abc-123" + no_special_chars_except_space = "abc efg - 123" + + self.assertRegex(data, DataRegex.DATA) + self.assertRegex(raw, DataRegex.RAW) + self.assertRegex(derived, DataRegex.DERIVED) + self.assertRegex(analyzed, DataRegex.ANALYZED) + self.assertRegex(no_underscores, DataRegex.NO_UNDERSCORES) + self.assertRegex(no_special_chars, DataRegex.NO_SPECIAL_CHARS) + self.assertRegex(no_special_chars_except_space, DataRegex.NO_SPECIAL_CHARS_EXCEPT_SPACE) + + def test_patterns_fail(self): + """Tests that the regex patterns match unsuccessfully.""" + + malformed_data = "ecephys_2020-10-19_08:30:59" + malformed_raw = "ecephys_123455_2020-10-19_08-30-59_test" + malformed_derived = "ecephys_123455_2020-10-19_08-30-59_sorted_2020-11-21_09:31:58" + malformed_analyzed = "ecephys_123455_2020-10-19_08-30-59_sorted_2020-11-21_09-31-58_test" + malformed_no_underscores = "abc_123" + malformed_no_special_chars = "abc-123" + malformed_no_special_chars_except_space = "abc efg - 123 " + + self.assertNotRegex(malformed_data, DataRegex.DATA) + self.assertNotRegex(malformed_raw, DataRegex.RAW) + self.assertNotRegex(malformed_derived, DataRegex.DERIVED) + self.assertNotRegex(malformed_analyzed, DataRegex.ANALYZED) + self.assertNotRegex(malformed_no_underscores, DataRegex.NO_UNDERSCORES) + self.assertNotRegex(malformed_no_special_chars, DataRegex.NO_SPECIAL_CHARS) + self.assertNotRegex(malformed_no_special_chars_except_space, DataRegex.NO_SPECIAL_CHARS_EXCEPT_SPACE) + + +class TestDataNamePatternsMethods(unittest.TestCase): + """Tests methods in data_name_patterns module""" + + def test_datetime_to_name_string(self): + """Tests datetime object is converted to string""" + + dt = datetime(2020, 12, 29, 1, 10, 50) + actual_output = datetime_to_name_string(dt) + self.assertEqual("2020-12-29_01-10-50", actual_output) + + def test_datetime_with_tz_to_name_string(self): + """Tests datetime object with timezone is converted to string""" + + dt = datetime(2020, 12, 29, 1, 10, 50, tzinfo=timezone.utc) + actual_output = datetime_to_name_string(dt) + self.assertEqual("2020-12-29_01-10-50", actual_output) + + def test_datetime_from_name_string(self): + """Tests date and time strings are converted to datetime object""" + + input_date_str = "2020-12-29" + input_time_str = "01-10-50" + actual_output = datetime_from_name_string(d=input_date_str, t=input_time_str) + dt = datetime(2020, 12, 29, 1, 10, 50) + self.assertEqual(dt, actual_output) + + def test_build_data_name(self): + """Tests datetime object is converted to string and attached to label""" + + label = "ecephys_123456" + dt = datetime(2020, 12, 29, 1, 10, 50) + actual_output = build_data_name(label=label, creation_datetime=dt) - self.assertNotRegex(deformed_date, RegexParts.DATE) - self.assertNotRegex(deformed_time, RegexParts.TIME) + self.assertEqual("ecephys_123456_2020-12-29_01-10-50", actual_output) if __name__ == "__main__":