From 78bf7d051cb28a741575c9611b44c4a1158f74fc Mon Sep 17 00:00:00 2001 From: Samuel Gratzl Date: Thu, 5 Aug 2021 12:13:50 -0400 Subject: [PATCH] fix: timepair for week values not working --- requirements.txt | 1 + src/server/_params.py | 5 +++-- src/server/utils/__init__.py | 2 +- src/server/utils/dates.py | 23 ++++++++++++++++++++++- tests/server/utils/test_dates.py | 17 ++++++++++++++++- 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 277f60a37..6de313ae5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ pandas==1.2.3 scipy==1.6.2 tenacity==7.0.0 newrelic +epiweeks==2.1.2 diff --git a/src/server/_params.py b/src/server/_params.py index eabb3a33c..5505b2714 100644 --- a/src/server/_params.py +++ b/src/server/_params.py @@ -4,10 +4,9 @@ from typing import List, Optional, Sequence, Tuple, Union from flask import request -from numpy import sign from ._exceptions import ValidationFailedException -from .utils import days_in_range +from .utils import days_in_range, weeks_in_range def _parse_common_multi_arg(key: str) -> List[Tuple[str, Union[bool, Sequence[str]]]]: @@ -116,6 +115,8 @@ def count(self) -> float: """ if isinstance(self.time_values, bool): return inf if self.time_values else 0 + if self.time_type == 'week': + return sum(1 if isinstance(v, int) else weeks_in_range(v) for v in self.time_values) return sum(1 if isinstance(v, int) else days_in_range(v) for v in self.time_values) diff --git a/src/server/utils/__init__.py b/src/server/utils/__init__.py index 0693f6bad..2cf5e85d9 100644 --- a/src/server/utils/__init__.py +++ b/src/server/utils/__init__.py @@ -1 +1 @@ -from .dates import shift_time_value, date_to_time_value, time_value_to_iso, time_value_to_date, days_in_range +from .dates import shift_time_value, date_to_time_value, time_value_to_iso, time_value_to_date, days_in_range, weeks_in_range diff --git a/src/server/utils/dates.py b/src/server/utils/dates.py index f48e5555e..2e2ad624d 100644 --- a/src/server/utils/dates.py +++ b/src/server/utils/dates.py @@ -1,5 +1,6 @@ from typing import Tuple from datetime import date, timedelta +from epiweeks import Week, Year def time_value_to_date(value: int) -> date: @@ -10,11 +11,21 @@ def time_value_to_date(value: int) -> date: return date.max return date(year=year, month=month, day=day) +def week_value_to_week(value: int) -> Week: + year, week = value // 100, value % 100 + if year < date.min.year: + return Week(date.min.year, 1) + if year > date.max.year: + return Week(date.max.year, 1) + return Week(year=year, week=week) def date_to_time_value(d: date) -> int: return int(d.strftime("%Y%m%d")) +def week_to_time_value(w: Week) -> int: + return w.year * 100 + w.week + def time_value_to_iso(value: int) -> str: return time_value_to_date(value).strftime("%Y-%m-%d") @@ -35,4 +46,14 @@ def days_in_range(range: Tuple[int, int]) -> int: start = time_value_to_date(range[0]) end = time_value_to_date(range[1]) delta = end - start - return delta.days + 1 # same date should lead to 1 day that will be queried \ No newline at end of file + return delta.days + 1 # same date should lead to 1 day that will be queried + +def weeks_in_range(week_range: Tuple[int, int]) -> int: + start = week_value_to_week(week_range[0]) + end = week_value_to_week(week_range[1]) + acc = end.week - start.week + # accumulate the number of weeks in the years between + for y in range(start.year, end.year): + year = Year(y) + acc += year.totalweeks() + return acc + 1 # same week should lead to 1 week that will be queried diff --git a/tests/server/utils/test_dates.py b/tests/server/utils/test_dates.py index 53735bf33..b4697510e 100644 --- a/tests/server/utils/test_dates.py +++ b/tests/server/utils/test_dates.py @@ -1,7 +1,8 @@ import unittest from datetime import date +from epiweeks import Week -from delphi.epidata.server.utils.dates import time_value_to_date, date_to_time_value, shift_time_value, time_value_to_iso, days_in_range +from delphi.epidata.server.utils.dates import time_value_to_date, date_to_time_value, shift_time_value, time_value_to_iso, days_in_range, weeks_in_range, week_to_time_value, week_value_to_week class UnitTests(unittest.TestCase): @@ -25,3 +26,17 @@ def test_days_in_range(self): self.assertEqual(days_in_range((20201010, 20201010)), 1) self.assertEqual(days_in_range((20201010, 20201011)), 2) self.assertEqual(days_in_range((20200130, 20200203)), 5) + + def test_weeks_in_range(self): + self.assertEqual(weeks_in_range((202110, 202110)), 1) + self.assertEqual(weeks_in_range((202110, 202112)), 3) + self.assertEqual(weeks_in_range((202001, 202101)), 54) # 2020 has 53 weeks + self.assertEqual(weeks_in_range((202101, 202204)), 56) + + def test_week_value_to_week(self): + self.assertEqual(week_value_to_week(202021), Week(2020, 21)) + self.assertEqual(week_value_to_week(202101), Week(2021, 1)) + + def test_week_to_time_value(self): + self.assertEqual(week_to_time_value(Week(2021, 1)), 202101) + self.assertEqual(week_to_time_value(Week(2020, 42)), 202042)