Skip to content

Commit

Permalink
Add report range functions for chunking requests and diffing cache by…
Browse files Browse the repository at this point in the history
… month
  • Loading branch information
kirkedev committed Jul 28, 2019
1 parent a34d06f commit 3d7b284
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 55 deletions.
12 changes: 0 additions & 12 deletions mpr/date.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
date64 = dtype('datetime64[D]')
Date = type(date64)
DateInterval = Tuple[date, date]
Quarter = Tuple[int, int]


def from_string(date_string: str, date_format: str) -> date64:
Expand All @@ -22,14 +21,3 @@ def to_ordinal(it: date64) -> uint32:

def from_ordinal(ordinal: uint32) -> date64:
return datetime64(date.fromordinal(ordinal), 'D')


def to_quarter(it: date) -> Quarter:
return it.year, (it.month - 1) // 3


def from_quarter(year: int, quarter: int) -> DateInterval:
month = quarter * 3 + 1
start = date(year, month, 1)
end = date(year, month + 3, 1) if 0 <= quarter < 3 else date(year + 1, 1, 1)
return start, end
64 changes: 64 additions & 0 deletions mpr/report_range.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from datetime import date
from typing import Tuple
from typing import NamedTuple
from typing import Union
from typing import Iterator
from .date import DateInterval

# Load Algorithm...
# given a date range...
# create a ReportMonth for each month in the request range
# Retrieve report months from table's fetched attribute that match the requested ReportMonth
# Find the date difference between each requested report range and the corresponding fetched report range
# Request the report for those dates and save it into the table
# Replace the fetched report month with the requested report month in table's fetched attribute

Month = Tuple[int, int]


class ReportMonth(NamedTuple):
year: int
month: int
end: int = None

@property
def dates(self) -> DateInterval:
year = self.year
month = self.month
end = self.end
last = date(year, month, end) if end else min(date(*next_month(year, month), 1), date.today())
return date(year, month, 1), last

def __hash__(self) -> int:
return hash((self.year, self.month))

def __eq__(self, other) -> bool:
return isinstance(other, ReportMonth) and hash(self) == hash(other) and self.end == other.end


def next_month(year: int, month: int) -> Month:
return (year, month + 1) if month < 12 else (year + 1, 1)


def get_months(start: date, end: date) -> Iterator[Month]:
current = (start.year, start.month)
last = (end.year, end.month)

while current <= last:
yield current
current = next_month(*current)


def report_months(start: date, end: date) -> Iterator[ReportMonth]:
return map(lambda it: ReportMonth(*it), get_months(start, end))


def report_diff(requested: ReportMonth, fetched: ReportMonth) -> Union[DateInterval, None]:
assert requested.month == fetched.month, "Reports must be the same month"

if requested == fetched:
return None

first = fetched.dates[1]
second = requested.dates[1]
return min(first, second), max(first, second)
43 changes: 0 additions & 43 deletions test/test_date.py

This file was deleted.

46 changes: 46 additions & 0 deletions test/test_report_range.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from datetime import date
from unittest import TestCase

from mpr.report_range import ReportMonth, next_month, report_months, get_months, report_diff


class ReportRangeTest(TestCase):
def test_equals(self):
first = ReportMonth(2019, 1)
second = ReportMonth(2019, 1)
self.assertEqual(first, second)

def test_not_equal(self):
first = ReportMonth(2019, 1)
second = ReportMonth(2019, 1, 15)
self.assertEqual(hash(first), hash(second))
self.assertNotEqual(first, second)

def test_next_month(self):
self.assertEqual(next_month(2018, 12), (2019, 1))
self.assertEqual(next_month(2019, 1), (2019, 2))

def test_months(self):
months = list(get_months(date(2018, 10, 1), date(2019, 2, 15)))
self.assertEqual(months, [(2018, 10), (2018, 11), (2018, 12), (2019, 1), (2019, 2)])

def test_report_months(self):
today = date.today()
year = today.year
month = today.month
first, *_, last = report_months(date(2019, 6, 1), today)

self.assertEqual(first, ReportMonth(2019, 6))
self.assertEqual(last.year, year)
self.assertEqual(last.month, month)
self.assertEqual(last.dates, (date(year, month, 1), today))

def test_report_diff(self):
with self.assertRaises(AssertionError):
report_diff(ReportMonth(2019, 1), ReportMonth(2019, 2))

self.assertIsNone(report_diff(ReportMonth(2019, 1), ReportMonth(2019, 1)))

start, end = report_diff(ReportMonth(2019, 1), ReportMonth(2019, 1, 15))
self.assertEqual(start, date(2019, 1, 15))
self.assertEqual(end, date(2019, 2, 1))

0 comments on commit 3d7b284

Please sign in to comment.