diff --git a/docs/uri-scheme-browse-origin.rst b/docs/uri-scheme-browse-origin.rst --- a/docs/uri-scheme-browse-origin.rst +++ b/docs/uri-scheme-browse-origin.rst @@ -91,8 +91,8 @@ to retrieve the root directory :query string revision: specify the origin revision, identified by the hexadecimal representation of its **sha1_git** value, from which to retrieve the root directory - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :query int visit_id: specify a visit id to retrieve the directory from instead of using the latest full visit by default :statuscode 200: no error @@ -109,9 +109,7 @@ :swh_web_browse:`origin/directory/?origin_url=https://github.com/python/cpython` :swh_web_browse:`origin/directory/?origin_url=https://github.com/python/cpython&path=Python` :swh_web_browse:`origin/directory/?origin_url=https://github.com/python/cpython&branch=refs/heads/2.7` - :swh_web_browse:`origin/directory/?origin_url=https://github.com/torvalds/linux×tamp=1493926809` :swh_web_browse:`origin/directory/?origin_url=https://github.com/torvalds/linux&path=net/ethernet×tamp=2016-09-14T10:36:21Z` - :swh_web_browse:`origin/directory/?origin_url=https://github.com/python/cpython×tamp=1474620651` :swh_web_browse:`origin/directory/?origin_url=https://github.com/python/cpython&path=Python×tamp=2017-05-05` :swh_web_browse:`origin/directory/?origin_url=https://github.com/python/cpython&branch=refs/heads/2.7×tamp=2015-08` @@ -192,8 +190,8 @@ content can also be specified by using the branch query parameter. :param string origin_url: the url of the origin (e.g. https://github.com/(user)/(repo)/) - :param string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :param string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :param path: optional parameter used to specify the path of a directory reachable from the origin root one :type path: string @@ -259,8 +257,8 @@ to retrieve the content :query string revision: specify the origin revision, identified by the hexadecimal representation of its **sha1_git** value, from which to retrieve the content - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :query int visit_id: specify a visit id to retrieve the content from instead of using the latest full visit by default :statuscode 200: no error @@ -275,9 +273,7 @@ :swh_web_browse:`origin/content/?origin_url=https://github.com/git/git?path=git.c` :swh_web_browse:`origin/content/?origin_url=https://github.com/mozilla/gecko-dev&path=js/src/json.cpp` :swh_web_browse:`origin/content/?origin_url=https://github.com/git/git?path=git.c&branch=refs/heads/next` - :swh_web_browse:`origin/content/?origin_url=https://github.com/git/git&path=git.c×tamp=1473933564` :swh_web_browse:`origin/content/?origin_url=https://github.com/git/git&path=git.c×tamp=2016-05-05T00:0:00+00:00Z` - :swh_web_browse:`origin/content/?origin_url=https://github.com/mozilla/gecko-dev&path=js/src/json.cpp×tamp=1490126182` :swh_web_browse:`origin/content/?origin_url=https://github.com/mozilla/gecko-dev&path=js/src/json.cpp×tamp=2017-03-21#L904-L931` :swh_web_browse:`origin/content/?origin_url=https://github.com/git/git&path=git.c&branch=refs/heads/next×tamp=2017-09-15` @@ -320,8 +316,8 @@ to retrieve the content :query string revision: specify the origin revision, identified by the hexadecimal representation of its **sha1_git** value, from which to retrieve the content - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :query int visit_id: specify a visit id to retrieve the content from instead of using the latest full visit by default :statuscode 200: no error @@ -336,9 +332,7 @@ :swh_web_browse:`origin/https://github.com/git/git/content/?path=git.c` :swh_web_browse:`origin/https://github.com/mozilla/gecko-dev/content/?path=js/src/json.cpp` :swh_web_browse:`origin/https://github.com/git/git/content/?path=git.c&branch=refs/heads/next` - :swh_web_browse:`origin/https://github.com/git/git/content/?path=git.c×tamp=1473933564` :swh_web_browse:`origin/https://github.com/git/git/content/?path=git.c×tamp=2016-05-05T00:0:00+00:00Z` - :swh_web_browse:`origin/https://github.com/mozilla/gecko-dev/content?path=js/src/json.cpp×tamp=1490126182` :swh_web_browse:`origin/https://github.com/mozilla/gecko-dev/content?path=js/src/json.cpp×tamp=2017-03-21#L904-L931` :swh_web_browse:`origin/https://github.com/git/git/content/git.c/?branch=refs/heads/next×tamp=2017-09-15` @@ -428,8 +422,8 @@ can also be specified by using the branch query parameter. :param string origin_url: the url of the origin (e.g. https://github.com/(user)/(repo)/) - :param string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :param string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :param string path: path of a content reachable from the origin root directory :query string branch: specify the origin branch name from which to retrieve the content @@ -448,9 +442,7 @@ .. parsed-literal:: - :swh_web_browse:`origin/https://github.com/git/git/visit/1473933564/content/git.c/` :swh_web_browse:`origin/https://github.com/git/git/visit/2016-05-05T00:0:00+00:00Z/content/git.c/` - :swh_web_browse:`origin/https://github.com/mozilla/gecko-dev/visit/1490126182/content/js/src/json.cpp/` :swh_web_browse:`origin/https://github.com/mozilla/gecko-dev/visit/2017-03-21/content/js/src/json.cpp/#L904-L931` :swh_web_browse:`origin/https://github.com/git/git/visit/2017-09-15/content/git.c/?branch=refs/heads/next` @@ -502,8 +494,8 @@ to retrieve the commit log :query string revision: specify the origin revision, identified by the hexadecimal representation of its **sha1_git** value, from which to retrieve the commit log - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :query int visit_id: specify a visit id to retrieve the history log from instead of using the latest visit by default :statuscode 200: no error @@ -571,8 +563,8 @@ to retrieve the commit log :query string revision: specify the origin revision, identified by the hexadecimal representation of its **sha1_git** value, from which to retrieve the commit log - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :query int visit_id: specify a visit id to retrieve the history log from instead of using the latest visit by default :statuscode 200: no error @@ -585,9 +577,7 @@ :swh_web_browse:`origin/https://github.com/videolan/vlc/log/` :swh_web_browse:`origin/https://github.com/Kitware/CMake/log/` :swh_web_browse:`origin/https://github.com/Kitware/CMake/log/?branch=refs/heads/release` - :swh_web_browse:`origin/https://github.com/videolan/vlc/log/?visit=1459651262` :swh_web_browse:`origin/https://github.com/Kitware/CMake/log/?timestamp=2016-04-01` - :swh_web_browse:`origin/https://github.com/Kitware/CMake/log/?branch=refs/heads/release×tamp=1438116814` :swh_web_browse:`origin/https://github.com/Kitware/CMake/log/?branch=refs/heads/release×tamp=2017-05-05T03:14:23Z` @@ -626,8 +616,8 @@ can also be specified by using the branch query parameter. :param string origin_url: the url of the origin (e.g. https://github.com/(user)/(repo)/) - :param string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :param string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :query int per_page: the number of log entries to display per page (default is 20, max is 50) :query string branch: specify the origin branch name from which @@ -645,9 +635,7 @@ .. parsed-literal:: - :swh_web_browse:`origin/https://github.com/videolan/vlc/visit/1459651262/log/` :swh_web_browse:`origin/https://github.com/Kitware/CMake/visit/2016-04-01/log/` - :swh_web_browse:`origin/https://github.com/Kitware/CMake/visit/1438116814/log/?branch=refs/heads/release` :swh_web_browse:`origin/https://github.com/Kitware/CMake/visit/2017-05-05T03:14:23Z/log/?branch=refs/heads/release` Origin branches @@ -670,8 +658,8 @@ :query string origin_url: mandatory parameter providing the url of the origin (e.g. https://github.com/(user)/(repo)) - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :statuscode 200: no error :statuscode 400: no origin url has been provided as parameter :statuscode 404: requested origin can not be found in the archive @@ -706,8 +694,8 @@ That list of branches is paginated, each page displaying a maximum of 100 branches. :param string origin_url: the url of the origin (e.g. https://github.com/(user)/(repo)/) - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :statuscode 200: no error :statuscode 404: requested origin can not be found in the archive @@ -741,8 +729,8 @@ That list of branches is paginated, each page displaying a maximum of 100 branches. :param string origin_url: the url of the origin (e.g. https://github.com/(user)/(repo)/) - :param string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :param string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :statuscode 200: no error :statuscode 404: requested origin can not be found in the archive @@ -773,8 +761,8 @@ :query string origin_url: mandatory parameter providing the url of the origin (e.g. https://github.com/(user)/(repo)) - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :statuscode 200: no error :statuscode 400: no origin url has been provided as parameter :statuscode 404: requested origin can not be found in the archive @@ -809,8 +797,8 @@ That list of releases is paginated, each page displaying a maximum of 100 releases. :param string origin_url: the url of the origin (e.g. https://github.com/(user)/(repo)/) - :query string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :statuscode 200: no error :statuscode 404: requested origin can not be found in the archive @@ -844,8 +832,8 @@ That list of releases is paginated, each page displaying a maximum of 100 releases. :param string origin_url: the url of the origin (e.g. https://github.com/(user)/(repo)/) - :param string timestamp: a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. + :param string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. :statuscode 200: no error :statuscode 404: requested origin can not be found in the archive @@ -858,4 +846,3 @@ .. _highlightjs: https://highlightjs.org/ -.. _dateutil.parser.parse: http://dateutil.readthedocs.io/en/stable/parser.html diff --git a/docs/uri-scheme-browse-revision.rst b/docs/uri-scheme-browse-revision.rst --- a/docs/uri-scheme-browse-revision.rst +++ b/docs/uri-scheme-browse-revision.rst @@ -20,13 +20,11 @@ identifier of a revision :query string origin_url: used internally to associate an origin url (e.g. https://github.com/user/repo) to the revision - :query string timestamp: used internally to associate an origin visit to the - revision, must be a date string (any format parsable by `dateutil.parser.parse`_) - or Unix timestamp to parse in order to find the closest visit. - :query int visit_id: used internally to specify a visit id instead of + :query string timestamp: an ISO 8601 datetime string to parse in order to find the + closest visit. + :query int visit_id: specify a visit id instead of using the provided timestamp - :query string path: used internally when navigating in the source tree - associated to the revision + :query string path: optional relative path from the revision root directory :statuscode 200: no error :statuscode 404: requested revision can not be found in the archive @@ -37,8 +35,6 @@ :swh_web_browse:`revision/f1b94134a4b879bc55c3dacdb496690c8ebdc03f/` :swh_web_browse:`revision/d1aa2b3f607b35dc5dbf613b2334b6d243ec2bda/` - .. _dateutil.parser.parse: http://dateutil.readthedocs.io/en/stable/parser.html - .. http:get:: /browse/revision/(sha1_git)/log/ HTML view that displays the list of revisions heading to diff --git a/docs/uri-scheme-browse.rst b/docs/uri-scheme-browse.rst --- a/docs/uri-scheme-browse.rst +++ b/docs/uri-scheme-browse.rst @@ -69,7 +69,6 @@ principle of least surprise. At the very minimum it is possible to enter timestamps as: - - Unix epoch timestamp (see for instance the output of `date +%s`) - ISO 8601 timestamps (see for instance the output of `date -I`, `date -Is`) - YYYY[MM[DD[HH[MM[SS]]]]] ad-hoc format - YYYY[-MM[-DD[ HH:[MM:[SS:]]]]] ad-hoc format diff --git a/mypy.ini b/mypy.ini --- a/mypy.ini +++ b/mypy.ini @@ -21,6 +21,9 @@ [mypy-htmlmin.*] ignore_missing_imports = True +[mypy-iso8601.*] +ignore_missing_imports = True + [mypy-keycloak.*] ignore_missing_imports = True diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -10,11 +10,11 @@ django-webpack-loader docutils htmlmin +iso8601 lxml prometheus-client pybadges pygments -python-dateutil python-keycloak >= 0.19.0 python-magic >= 0.4.0 python-memcached diff --git a/swh/web/browse/snapshot_context.py b/swh/web/browse/snapshot_context.py --- a/swh/web/browse/snapshot_context.py +++ b/swh/web/browse/snapshot_context.py @@ -7,7 +7,7 @@ from collections import defaultdict from copy import copy -from typing import Any, Dict, List, Optional, Union, Tuple +from typing import Any, Dict, List, Optional, Tuple from django.core.cache import cache @@ -348,7 +348,7 @@ def get_origin_visit_snapshot( origin_info: OriginInfo, - visit_ts: Optional[Union[int, str]] = None, + visit_ts: Optional[str] = None, visit_id: Optional[int] = None, snapshot_id: Optional[str] = None, ) -> Tuple[List[SnapshotBranchInfo], List[SnapshotReleaseInfo]]: @@ -372,7 +372,7 @@ Args: origin_info: a dict filled with origin information - visit_ts: an ISO date string or Unix timestamp to parse + visit_ts: an ISO 8601 datetime string to parse visit_id: visit id for disambiguation in case several visits have the same timestamp snapshot_id: if provided, visit associated to the snapshot will be processed diff --git a/swh/web/browse/views/origin.py b/swh/web/browse/views/origin.py --- a/swh/web/browse/views/origin.py +++ b/swh/web/browse/views/origin.py @@ -18,7 +18,7 @@ from swh.web.common import service from swh.web.common.exc import handle_view_exception, BadInputExc from swh.web.common.origin_visits import get_origin_visits -from swh.web.common.utils import reverse, format_utc_iso_date, parse_timestamp +from swh.web.common.utils import reverse, format_utc_iso_date, parse_iso8601_date_to_utc @browse_route( @@ -255,7 +255,7 @@ visit["url"] = reverse("browse-origin-directory", query_params=query_params,) if not snapshot: visit["snapshot"] = "" - visit["date"] = parse_timestamp(visit["date"]).timestamp() + visit["date"] = parse_iso8601_date_to_utc(visit["date"]).timestamp() heading = "Origin visits - %s" % origin_url diff --git a/swh/web/common/origin_save.py b/swh/web/common/origin_save.py --- a/swh/web/common/origin_save.py +++ b/swh/web/common/origin_save.py @@ -38,7 +38,7 @@ SAVE_TASK_NOT_CREATED, ) from swh.web.common.origin_visits import get_origin_visits -from swh.web.common.utils import parse_timestamp, SWH_WEB_METRICS_REGISTRY +from swh.web.common.utils import parse_iso8601_date_to_utc, SWH_WEB_METRICS_REGISTRY from swh.scheduler.utils import create_oneshot_task_dict @@ -159,7 +159,7 @@ origin = {"url": save_request.origin_url} origin_info = service.lookup_origin(origin) origin_visits = get_origin_visits(origin_info) - visit_dates = [parse_timestamp(v["date"]) for v in origin_visits] + visit_dates = [parse_iso8601_date_to_utc(v["date"]) for v in origin_visits] i = bisect_right(visit_dates, save_request.request_date) if i != len(visit_dates): visit_date = visit_dates[i] diff --git a/swh/web/common/origin_visits.py b/swh/web/common/origin_visits.py --- a/swh/web/common/origin_visits.py +++ b/swh/web/common/origin_visits.py @@ -4,13 +4,13 @@ # See top-level LICENSE file for more information import math -from typing import List, Optional, Union +from typing import List, Optional from django.core.cache import cache from swh.web.common.exc import NotFoundExc from swh.web.common.typing import OriginInfo, OriginVisitInfo -from swh.web.common.utils import parse_timestamp +from swh.web.common.utils import parse_iso8601_date_to_utc def get_origin_visits(origin_info: OriginInfo) -> List[OriginVisitInfo]: @@ -71,7 +71,7 @@ last_visit += per_page def _visit_sort_key(visit): - ts = parse_timestamp(visit["date"]).timestamp() + ts = parse_iso8601_date_to_utc(visit["date"]).timestamp() return ts + (float(visit["visit"]) / 10e3) origin_visits = sorted(origin_visits, key=lambda v: _visit_sort_key(v)) @@ -83,7 +83,7 @@ def get_origin_visit( origin_info: OriginInfo, - visit_ts: Optional[Union[int, str]] = None, + visit_ts: Optional[str] = None, visit_id: Optional[int] = None, snapshot_id: Optional[str] = None, ) -> OriginVisitInfo: @@ -101,7 +101,7 @@ Args: origin_info: a dict filled with origin information - visit_ts: an ISO date string or Unix timestamp to parse + visit_ts: an ISO 8601 datetime string to parse snapshot_id: a snapshot identifier Returns: @@ -161,12 +161,12 @@ if visit_ts: - target_visit_ts = math.floor(parse_timestamp(visit_ts).timestamp()) + target_visit_ts = math.floor(parse_iso8601_date_to_utc(visit_ts).timestamp()) # Find the visit with date closest to the target (in absolute value) (abs_time_delta, visit_idx) = min( ( - (math.floor(parse_timestamp(visit["date"]).timestamp()), i) + (math.floor(parse_iso8601_date_to_utc(visit["date"]).timestamp()), i) for (i, visit) in enumerate(visits) ), key=lambda ts_and_i: abs(ts_and_i[0] - target_visit_ts), diff --git a/swh/web/common/utils.py b/swh/web/common/utils.py --- a/swh/web/common/utils.py +++ b/swh/web/common/utils.py @@ -6,9 +6,6 @@ import re from datetime import datetime, timezone -from dateutil import parser as date_parser -from dateutil import tz - from typing import Optional, Dict, Any import docutils.parsers.rst @@ -22,6 +19,8 @@ from django.urls import reverse as django_reverse from django.http import QueryDict, HttpRequest +from iso8601 import parse_date, ParseError + from prometheus_client.registry import CollectorRegistry from rest_framework.authentication import SessionAuthentication @@ -104,39 +103,32 @@ Returns: datetime.datetime: datetime in UTC without timezone info """ - if date.tzinfo: - return date.astimezone(tz.gettz("UTC")).replace(tzinfo=timezone.utc) + if date.tzinfo and date.tzinfo != timezone.utc: + return date.astimezone(tz=timezone.utc) else: return date -def parse_timestamp(timestamp): - """Given a time or timestamp (as string), parse the result as UTC datetime. +def parse_iso8601_date_to_utc(iso_date: str) -> datetime: + """Given an ISO 8601 datetime string, parse the result as UTC datetime. Returns: - datetime.datetime: a timezone-aware datetime representing the - parsed value or None if the parsing fails. + a timezone-aware datetime representing the parsed date + + Raises: + swh.web.common.exc.BadInputExc: provided date does not respect ISO 8601 format Samples: - 2016-01-12 - 2016-01-12T09:19:12+0100 - - Today is January 1, 2047 at 8:21:00AM - - 1452591542 + - 2007-01-14T20:34:22Z """ - if not timestamp: - return None - try: - date = date_parser.parse(timestamp, ignoretz=False, fuzzy=True) + date = parse_date(iso_date) return datetime_to_utc(date) - except Exception: - try: - return datetime.utcfromtimestamp(float(timestamp)).replace( - tzinfo=timezone.utc - ) - except (ValueError, OverflowError) as e: - raise BadInputExc(e) + except ParseError as e: + raise BadInputExc(e) def shorten_path(path): @@ -151,7 +143,7 @@ def format_utc_iso_date(iso_date, fmt="%d %B %Y, %H:%M UTC"): - """Turns a string representation of an ISO 8601 date string + """Turns a string representation of an ISO 8601 datetime string to UTC and format it into a more human readable one. For instance, from the following input @@ -169,7 +161,7 @@ """ if not iso_date: return iso_date - date = parse_timestamp(iso_date) + date = parse_iso8601_date_to_utc(iso_date) return date.strftime(fmt) diff --git a/swh/web/tests/browse/views/test_origin.py b/swh/web/tests/browse/views/test_origin.py --- a/swh/web/tests/browse/views/test_origin.py +++ b/swh/web/tests/browse/views/test_origin.py @@ -29,7 +29,7 @@ reverse, gen_path_info, format_utc_iso_date, - parse_timestamp, + parse_iso8601_date_to_utc, ) from swh.web.tests.data import get_content, random_sha1 from swh.web.tests.django_asserts import assert_contains, assert_template_used @@ -118,21 +118,6 @@ timestamp=tdata["visit"]["date"], ) - visit_unix_ts = parse_timestamp(tdata["visit"]["date"]).timestamp() - visit_unix_ts = int(visit_unix_ts) - - _origin_content_view_test_helper( - client, - archive_data, - origin, - origin_visits[-1], - tdata["branches"], - tdata["releases"], - tdata["root_dir_sha1"], - tdata["content"], - timestamp=visit_unix_ts, - ) - _origin_content_view_test_helper( client, archive_data, @@ -183,8 +168,6 @@ root_dir_sha1 = head_rev["directory"] dir_content = archive_data.directory_ls(root_dir_sha1) branches, releases = process_snapshot_branches(snapshot) - visit_unix_ts = parse_timestamp(visit["date"]).timestamp() - visit_unix_ts = int(visit_unix_ts) _origin_directory_view_test_helper( client, @@ -209,18 +192,6 @@ visit_id=visit["visit"], ) - _origin_directory_view_test_helper( - client, - archive_data, - origin, - visit, - branches, - releases, - root_dir_sha1, - dir_content, - timestamp=visit_unix_ts, - ) - _origin_directory_view_test_helper( client, archive_data, @@ -268,18 +239,6 @@ visit_id=visit["visit"], ) - _origin_directory_view_test_helper( - client, - archive_data, - origin, - visit, - branches, - releases, - root_dir_sha1, - dir_content, - timestamp=visit_unix_ts, - ) - _origin_directory_view_test_helper( client, archive_data, @@ -318,8 +277,6 @@ e for e in archive_data.directory_ls(root_dir_sha1) if e["type"] == "dir" ] branches, releases = process_snapshot_branches(snapshot) - visit_unix_ts = parse_timestamp(visit["date"]).timestamp() - visit_unix_ts = int(visit_unix_ts) if len(subdirs) == 0: return @@ -353,19 +310,6 @@ visit_id=visit["visit"], ) - _origin_directory_view_test_helper( - client, - archive_data, - origin, - visit, - branches, - releases, - root_dir_sha1, - subdir_content, - path=subdir_path, - timestamp=visit_unix_ts, - ) - _origin_directory_view_test_helper( client, archive_data, @@ -417,19 +361,6 @@ visit_id=visit["visit"], ) - _origin_directory_view_test_helper( - client, - archive_data, - origin, - visit, - branches, - releases, - root_dir_sha1, - subdir_content, - path=subdir_path, - timestamp=visit_unix_ts, - ) - _origin_directory_view_test_helper( client, archive_data, @@ -935,7 +866,7 @@ if timestamp: query_params["timestamp"] = format_utc_iso_date( - parse_timestamp(timestamp).isoformat(), "%Y-%m-%dT%H:%M:%SZ" + parse_iso8601_date_to_utc(timestamp).isoformat(), "%Y-%m-%dT%H:%M:%SZ" ) root_dir_url = reverse("browse-origin-directory", query_params=query_params) @@ -1069,7 +1000,7 @@ if timestamp: query_params["timestamp"] = format_utc_iso_date( - parse_timestamp(timestamp).isoformat(), "%Y-%m-%dT%H:%M:%SZ" + parse_iso8601_date_to_utc(timestamp).isoformat(), "%Y-%m-%dT%H:%M:%SZ" ) for d in dirs: diff --git a/swh/web/tests/browse/views/test_revision.py b/swh/web/tests/browse/views/test_revision.py --- a/swh/web/tests/browse/views/test_revision.py +++ b/swh/web/tests/browse/views/test_revision.py @@ -10,7 +10,7 @@ from swh.model.identifiers import DIRECTORY, REVISION, SNAPSHOT from swh.web.common.identifiers import gen_swhid -from swh.web.common.utils import reverse, format_utc_iso_date, parse_timestamp +from swh.web.common.utils import reverse, format_utc_iso_date, parse_iso8601_date_to_utc from swh.web.tests.django_asserts import assert_contains, assert_template_used from swh.web.tests.strategies import origin, revision, unknown_revision, new_origin @@ -43,7 +43,7 @@ revision_log_sorted = sorted( revision_log, - key=lambda rev: -parse_timestamp(rev["committer_date"]).timestamp(), + key=lambda rev: -parse_iso8601_date_to_utc(rev["committer_date"]).timestamp(), ) url = reverse( diff --git a/swh/web/tests/common/test_origin_visits.py b/swh/web/tests/common/test_origin_visits.py --- a/swh/web/tests/common/test_origin_visits.py +++ b/swh/web/tests/common/test_origin_visits.py @@ -135,9 +135,6 @@ visit = get_origin_visit(origin_info, visit_ts="2016-06-18 01:22") assert visit == visits[3] - visit = get_origin_visit(origin_info, visit_ts=1466208000) - assert visit == visits[3] - visit = get_origin_visit(origin_info, visit_ts="2014-01-01") assert visit == visits[0] diff --git a/swh/web/tests/common/test_utils.py b/swh/web/tests/common/test_utils.py --- a/swh/web/tests/common/test_utils.py +++ b/swh/web/tests/common/test_utils.py @@ -5,7 +5,10 @@ import datetime +import pytest + from swh.web.common import utils +from swh.web.common.exc import BadInputExc def test_shorten_path_noop(): @@ -44,25 +47,33 @@ assert utils.shorten_path(template % sha256) == template % short_sha256 -def test_parse_timestamp(): - input_timestamps = [ - None, - "2016-01-12", - "2016-01-12T09:19:12+0100", - "Today is January 1, 2047 at 8:21:00AM", - "1452591542", - ] - - output_dates = [ - None, - datetime.datetime(2016, 1, 12, 0, 0), - datetime.datetime(2016, 1, 12, 8, 19, 12, tzinfo=datetime.timezone.utc), - datetime.datetime(2047, 1, 1, 8, 21), - datetime.datetime(2016, 1, 12, 9, 39, 2, tzinfo=datetime.timezone.utc), - ] - - for ts, exp_date in zip(input_timestamps, output_dates): - assert utils.parse_timestamp(ts) == exp_date +@pytest.mark.parametrize( + "input_timestamp, output_date", + [ + ( + "2016-01-12", + datetime.datetime(2016, 1, 12, 0, 0, tzinfo=datetime.timezone.utc), + ), + ( + "2016-01-12T09:19:12+0100", + datetime.datetime(2016, 1, 12, 8, 19, 12, tzinfo=datetime.timezone.utc), + ), + ( + "2007-01-14T20:34:22Z", + datetime.datetime(2007, 1, 14, 20, 34, 22, tzinfo=datetime.timezone.utc), + ), + ], +) +def test_parse_iso8601_date_to_utc_ok(input_timestamp, output_date): + assert utils.parse_iso8601_date_to_utc(input_timestamp) == output_date + + +@pytest.mark.parametrize( + "invalid_iso8601_timestamp", ["Today is January 1, 2047 at 8:21:00AM", "1452591542"] +) +def test_parse_iso8601_date_to_utc_ko(invalid_iso8601_timestamp): + with pytest.raises(BadInputExc): + utils.parse_iso8601_date_to_utc(invalid_iso8601_timestamp) def test_format_utc_iso_date():