From 3e86b5d5318d757571076760097d22e3316f4b61 Mon Sep 17 00:00:00 2001 From: Benjamin Pritchard Date: Tue, 7 Jan 2025 22:02:01 -0500 Subject: [PATCH] Add function to convert duration string to int --- qcportal/qcportal/test_utils.py | 32 +++++++++++++++++++++++++++++++- qcportal/qcportal/utils.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/qcportal/qcportal/test_utils.py b/qcportal/qcportal/test_utils.py index 4f7cf03fa..337b58d09 100644 --- a/qcportal/qcportal/test_utils.py +++ b/qcportal/qcportal/test_utils.py @@ -1,4 +1,4 @@ -from qcportal.utils import chunk_iterable, seconds_to_hms, is_included +from qcportal.utils import chunk_iterable, seconds_to_hms, duration_to_seconds, is_included def test_chunk_iterable(): @@ -29,6 +29,36 @@ def test_seconds_to_hms(): assert seconds_to_hms(3670.12) == "01:01:10.12" +def test_duration_to_seconds(): + assert duration_to_seconds(0) == 0 + assert duration_to_seconds("0") == 0 + assert duration_to_seconds(17) == 17 + assert duration_to_seconds("17") == 17 + + assert duration_to_seconds("17s") == 17 + assert duration_to_seconds("70s") == 70 + assert duration_to_seconds("8m17s") == 497 + assert duration_to_seconds("80m72s") == 4872 + assert duration_to_seconds("3h8m17s") == 11297 + assert duration_to_seconds("03h08m07s") == 11287 + assert duration_to_seconds("03h08m070s") == 11350 + assert duration_to_seconds("9d03h08m070s") == 788950 + + assert duration_to_seconds("9d") == 777600 + assert duration_to_seconds("10m") == 600 + assert duration_to_seconds("90m") == 5400 + assert duration_to_seconds("04h") == 14400 + assert duration_to_seconds("4h5s") == 14405 + assert duration_to_seconds("1d9s") == 86409 + + assert duration_to_seconds("8:17") == 497 + assert duration_to_seconds("80:72") == 4872 + assert duration_to_seconds("3:8:17") == 11297 + assert duration_to_seconds("03:08:07") == 11287 + assert duration_to_seconds("03:08:070") == 11350 + assert duration_to_seconds("9:03:08:07") == 788950 + + def test_is_included(): assert is_included("test", None, None, True) is True assert is_included("test", None, None, False) is False diff --git a/qcportal/qcportal/utils.py b/qcportal/qcportal/utils.py index 89806afd1..1192212e5 100644 --- a/qcportal/qcportal/utils.py +++ b/qcportal/qcportal/utils.py @@ -8,6 +8,7 @@ import json import logging import math +import re import time from contextlib import contextmanager, redirect_stderr, redirect_stdout from hashlib import sha256 @@ -261,6 +262,37 @@ def seconds_to_hms(seconds: Union[float, int]) -> str: return f"{hours:02d}:{minutes:02d}:{seconds+fraction:02.2f}" +def duration_to_seconds(s: Union[int, str]) -> int: + """ + Parses a string in dd:hh:mm:ss or 1d2h3m4s to an integer number of seconds + """ + + # Is already an int + if isinstance(s, int): + return s + + # Plain number of seconds (as a string) + if s.isdigit(): + return int(s) + + # Handle dd:hh:mm:ss format + if ":" in s: + parts = list(map(int, s.split(":"))) + while len(parts) < 4: # Pad missing parts with zeros + parts.insert(0, 0) + days, hours, minutes, seconds = parts + return days * 86400 + hours * 3600 + minutes * 60 + seconds + + # Handle format like 3d4h7m10s + pattern = re.compile(r"(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?") + match = pattern.fullmatch(s) + if not match: + raise ValueError(f"Invalid duration format: {s}") + + days, hours, minutes, seconds = map(lambda x: int(x) if x else 0, match.groups()) + return days * 86400 + hours * 3600 + minutes * 60 + seconds + + def recursive_normalizer(value: Any, digits: int = 10, lowercase: bool = True) -> Any: """ Prepare a structure for hashing by lowercasing all values and round all floats