From e55258c5bef616a9cef10bacc9e276fb4dda21ba Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Wed, 9 Aug 2023 21:52:49 -0500 Subject: [PATCH] fx: Nextcloud migration fails to parse null times (#2485) * support null or custom strings * made time parsing more robust using isodate * bark bark --- mealie/services/migrations/nextcloud.py | 50 ++++++++++++++++++------- poetry.lock | 2 +- pyproject.toml | 1 + 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/mealie/services/migrations/nextcloud.py b/mealie/services/migrations/nextcloud.py index c4d5678743f6..74d442285c08 100644 --- a/mealie/services/migrations/nextcloud.py +++ b/mealie/services/migrations/nextcloud.py @@ -1,9 +1,12 @@ -import re import tempfile import zipfile from dataclasses import dataclass +from datetime import timedelta from pathlib import Path +from typing import cast +import isodate +from isodate.isoerror import ISO8601Error from slugify import slugify from ._migration_base import BaseMigrator @@ -75,22 +78,43 @@ class NextcloudMigrator(BaseMigrator): import_image(nc_dir.image, recipe_id) -def parse_time(time: str) -> str: - """Parses a Nextcloud time string in the format 'PT{hours}H{minutes}M{seconds}S'""" +def parse_time(time: str | None) -> str: + """ + Parses an ISO8601 duration string + + https://en.wikipedia.org/wiki/ISO_8601#Durations + """ + + if not time: + return "" + if time[0] == "P": + try: + delta = isodate.parse_duration(time) + if not isinstance(delta, timedelta): + return time + except ISO8601Error: + return time # TODO: make singular and plural translatable - hours = {"singular": "hour", "plural": "hours", "exp": r"\d+(?=H)"} - minutes = {"singular": "minute", "plural": "minutes", "exp": r"\d+(?=M)"} - seconds = {"singular": "second", "plural": "seconds", "exp": r"\d+(?=S)"} + time_part_map = { + "days": {"singular": "day", "plural": "days"}, + "hours": {"singular": "hour", "plural": "hours"}, + "minutes": {"singular": "minute", "plural": "minutes"}, + "seconds": {"singular": "second", "plural": "seconds"}, + } + + delta = cast(timedelta, delta) + time_part_map["days"]["value"] = delta.days + time_part_map["hours"]["value"] = delta.seconds // 3600 + time_part_map["minutes"]["value"] = (delta.seconds // 60) % 60 + time_part_map["seconds"]["value"] = delta.seconds % 60 return_strings: list[str] = [] - for time_part in [hours, minutes, seconds]: - val_search = re.search(time_part["exp"], time) - if not val_search: + for value_map in time_part_map.values(): + if not (value := value_map["value"]): continue - val = val_search.group() - if val == "0": - continue - return_strings.append(f'{val} {time_part["singular"] if val == "1" else time_part["plural"]}') + + unit_key = "singular" if value == 1 else "plural" + return_strings.append(f"{value} {value_map[unit_key]}") return " ".join(return_strings) if return_strings else time diff --git a/poetry.lock b/poetry.lock index 06e0fcc177a3..5aae63777ea4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2935,4 +2935,4 @@ pgsql = ["psycopg2-binary"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "e25136f573af5b1b7ba61747a5bea6a4f2eb48f39b06e345cba03b14808d9258" +content-hash = "6ec46a82cad73c78013d1729df179a3fb5c4e12eb2a11ca04b2412d6b8e5b25f" diff --git a/pyproject.toml b/pyproject.toml index ce2da06b6b59..f6601bb6d18e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ requests = "^2.25.1" tzdata = "^2022.7" uvicorn = {extras = ["standard"], version = "^0.21.0"} beautifulsoup4 = "^4.11.2" +isodate = "^0.6.1" [tool.poetry.group.dev.dependencies] black = "^23.1.0"