From e540eee9b383d82dc610935f005d94761efa7d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Wed, 11 Oct 2023 15:46:05 +0200 Subject: [PATCH] Add the ability to convert to days, hours, minutes --- argos/schemas/utils.py | 41 +++++++++++++++++++++++-------------- tests/test_schemas_utils.py | 27 ++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/argos/schemas/utils.py b/argos/schemas/utils.py index a7e7699..b0bbaa8 100644 --- a/argos/schemas/utils.py +++ b/argos/schemas/utils.py @@ -1,30 +1,41 @@ from typing import Literal -def string_to_duration(value: str, target: Literal["days", "hours"]): - """Convert a string to a number of hours, or days""" +def string_to_duration(value: str, target: Literal["days", "hours", "minutes"]): + """Convert a string to a number of hours, days or minutes""" num = int("".join(filter(str.isdigit, value))) - if target == "hours": - reconvert = True + # It's not possible to convert from a smaller unit to a greater one: + # - hours and minutes cannot be converted to days + # - minutes cannot be converted to hours + if (target == "days" and ("h" in value or "m" in value.replace("mo", ""))) or ( + target == "hours" and "m" in value.replace("mo", "") + ): + msg = ( + "Durations cannot be converted from a smaller to a greater unit. " + f"(trying to convert '{value}' to {target})" + ) + raise ValueError(msg, value) + # Consider we're converting to minutes, do the eventual multiplication at the end. if "h" in value: - if target == "days": - raise ValueError("Invalid duration value", value) - num = num - reconvert = False + num = num * 60 elif "d" in value: - num = num + num = num * 60 * 24 elif "w" in value: - num = num * 7 - elif "m" in value: - num = num * 30 + num = num * 60 * 24 * 7 + elif "mo" in value: + num = num * 60 * 24 * 30 # considers 30d in a month elif "y" in value: - num = num * 365 + num = num * 60 * 24 * 365 + elif "m" in value: + num = num else: raise ValueError("Invalid duration value", value) - if target == "hours" and reconvert: - num = num * 24 + if target == "hours": + num = num / 60 + elif target == "days": + num = num / 60 / 24 return num diff --git a/tests/test_schemas_utils.py b/tests/test_schemas_utils.py index 120ca88..53cd170 100644 --- a/tests/test_schemas_utils.py +++ b/tests/test_schemas_utils.py @@ -7,7 +7,7 @@ def test_string_to_duration_days(): assert string_to_duration("1d", target="days") == 1 assert string_to_duration("1w", target="days") == 7 assert string_to_duration("3w", target="days") == 21 - assert string_to_duration("3m", target="days") == 90 + assert string_to_duration("3mo", target="days") == 90 assert string_to_duration("1y", target="days") == 365 with pytest.raises(ValueError): @@ -22,7 +22,30 @@ def test_string_to_duration_hours(): assert string_to_duration("1d", target="hours") == 24 assert string_to_duration("1w", target="hours") == 7 * 24 assert string_to_duration("3w", target="hours") == 21 * 24 - assert string_to_duration("3m", target="hours") == 3 * 30 * 24 + assert string_to_duration("3mo", target="hours") == 3 * 30 * 24 with pytest.raises(ValueError): string_to_duration("1", target="hours") + + +def test_string_to_duration_minutes(): + assert string_to_duration("1m", target="minutes") == 1 + assert string_to_duration("1h", target="minutes") == 60 + assert string_to_duration("1d", target="minutes") == 60 * 24 + assert string_to_duration("3mo", target="minutes") == 60 * 24 * 30 * 3 + + with pytest.raises(ValueError): + string_to_duration("1", target="minutes") + + +def test_conversion_to_greater_units_throws(): + # hours and minutes cannot be converted to days + with pytest.raises(ValueError): + string_to_duration("1h", target="days") + + with pytest.raises(ValueError): + string_to_duration("1m", target="days") + + # minutes cannot be converted to hours + with pytest.raises(ValueError): + string_to_duration("1m", target="hours")