From 4dff45ad4f5459e62fde51071bb03b7fbde12954 Mon Sep 17 00:00:00 2001 From: Matthew Lemon Date: Sun, 16 May 2021 16:53:13 +0100 Subject: working on Month class --- datamaps/api/__init__.py | 1 + datamaps/api/api.py | 24 +++++++++++ datamaps/core/temporal.py | 90 ++++++++++++++++++++++++++++++++---------- datamaps/plugins/dft/master.py | 8 ++++ datamaps/tests/test_api.py | 37 ++++++++++++++++- 5 files changed, 137 insertions(+), 23 deletions(-) diff --git a/datamaps/api/__init__.py b/datamaps/api/__init__.py index 3f6a35c..947f8a6 100644 --- a/datamaps/api/__init__.py +++ b/datamaps/api/__init__.py @@ -1 +1,2 @@ from .api import project_data_from_master_api as project_data_from_master +from .api import project_data_from_master_month_api as project_data_from_master_month diff --git a/datamaps/api/api.py b/datamaps/api/api.py index 6cc6f4e..c9f9a64 100644 --- a/datamaps/api/api.py +++ b/datamaps/api/api.py @@ -13,3 +13,27 @@ def project_data_from_master_api(master_file: str, quarter: int, year: int): """ m = Master(Quarter(quarter, year), master_file) return m + +def project_data_from_master_month_api(master_file: str, month: int, year: int): + """Create a Master object directly without the need to explicitly pass + a Month object. + + Args: + master_file (str): the path to a master file + month (int): an integer representing the month + year (int): an integer representing the year + """ + # we need to work out what Quarter we are dealing with from the month + if month in [1, 2, 3]: + quarter = 4 + elif month in [4, 5, 6]: + quarter = 1 + elif month in [7, 8, 9]: + quarter = 2 + elif month in [10, 11, 12]: + quarter = 3 + else: + pass + # TODO: raise exception here + m = Master(Quarter(quarter, year), master_file) + return m diff --git a/datamaps/core/temporal.py b/datamaps/core/temporal.py index ddb7004..d368f6a 100644 --- a/datamaps/core/temporal.py +++ b/datamaps/core/temporal.py @@ -1,4 +1,56 @@ import datetime +import calendar + + +class Month: + """An object representing a calendar Month.""" + + _end_ints = { + 1: 31, + 2: 28, + 3: 31, + 4: 30, + 5: 31, + 6: 30, + 7: 31, + 8: 31, + 9: 30, + 10: 31, + 11: 30, + 12: 31, + } + + def __init__(self, month: int, year: int): + self._month = month + self.year = year + + @property + def start_date(self): + return datetime.date(self.year, self._month, 1) + + @property + def end_date(self): + if self._month == 2 and calendar.isleap(self.year): + return datetime.date(self.year, self._month, Month._end_ints[self._month] + 1) + else: + return datetime.date(self.year, self._month, Month._end_ints[self._month]) + + @property + def month(self): + return [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ][self._month - 1] class FinancialYear: @@ -27,26 +79,22 @@ class FinancialYear: @property def q1(self) -> datetime.date: - """Quarter 1 as a :py:class:`datetime.date` object - """ + """Quarter 1 as a :py:class:`datetime.date` object""" return self._q1 @property def q2(self): - """Quarter 2 as a :py:class:`datetime.date` object - """ + """Quarter 2 as a :py:class:`datetime.date` object""" return self._q2 @property def q3(self): - """Quarter 3 as a :py:class:`datetime.date` object - """ + """Quarter 3 as a :py:class:`datetime.date` object""" return self._q3 @property def q4(self): - """Quarter 4 as a :py:class:`datetime.date` object - """ + """Quarter 4 as a :py:class:`datetime.date` object""" return self._q4 def __str__(self): @@ -55,7 +103,6 @@ class FinancialYear: def _generate_quarters(self): self.quarters = [Quarter(x, self.year) for x in range(1, 5)] - def __repr__(self): return f"FinancialYear({self.year})" @@ -68,21 +115,21 @@ class Quarter: quarter (int): e.g.1, 2, 3 or 4 year (int): e.g. 2013 """ + _start_months = { - 1: (4, 'April'), - 2: (7, 'July'), - 3: (10, 'October'), - 4: (1, 'January') + 1: (4, "April"), + 2: (7, "July"), + 3: (10, "October"), + 4: (1, "January"), } _end_months = { - 1: (6, 'June', 30), - 2: (9, 'September', 30), - 3: (12, 'December', 31), - 4: (3, 'March', 31), + 1: (6, "June", 30), + 2: (9, "September", 30), + 3: (12, "December", 31), + 4: (3, "March", 31), } - def __init__(self, quarter: int, year: int) -> None: if isinstance(quarter, int) and (quarter >= 1 and quarter <= 4): @@ -93,7 +140,9 @@ class Quarter: if isinstance(year, int) and (year in range(1950, 2100)): self.year = year else: - raise ValueError("Year must be between 1950 and 2100 - surely that will do?") + raise ValueError( + "Year must be between 1950 and 2100 - surely that will do?" + ) self.start_date = self._start_date(self.quarter, self.year) self.end_date = self._end_date(self.quarter, self.year) @@ -116,6 +165,5 @@ class Quarter: @property def fy(self): - """Return a :py:class:`core.temporal.FinancialYear` object. - """ + """Return a :py:class:`core.temporal.FinancialYear` object.""" return FinancialYear(self.year) diff --git a/datamaps/plugins/dft/master.py b/datamaps/plugins/dft/master.py index 744cc27..c3e6c3b 100644 --- a/datamaps/plugins/dft/master.py +++ b/datamaps/plugins/dft/master.py @@ -157,6 +157,14 @@ class Master: return self._quarter + @property + def month(self): + """ + Returns the ``Month`` object associated with the ``Master``. + """ + pass + + @property def filename(self): """The filename of the master xlsx file, e.g. ``master_1_2017.xlsx``. diff --git a/datamaps/tests/test_api.py b/datamaps/tests/test_api.py index 273f984..21ab4d1 100644 --- a/datamaps/tests/test_api.py +++ b/datamaps/tests/test_api.py @@ -1,10 +1,43 @@ import datetime -from ..api import project_data_from_master +from ..api import project_data_from_master, project_data_from_master_month +from ..core.temporal import Quarter, Month def test_get_project_data(master): master = project_data_from_master(master, 1, 2019) - assert master["Chutney Bridge.xlsm"]["Project/Programme Name"] == "Chutney Bridge Ltd" + assert ( + master["Chutney Bridge.xlsm"]["Project/Programme Name"] == "Chutney Bridge Ltd" + ) assert master.quarter.quarter == 1 assert master.quarter.end_date == datetime.date(2019, 6, 30) + + +def test_get_project_data_using_month(master): + master = project_data_from_master_month(master, 7, 2021) + assert ( + master["Chutney Bridge.xlsm"]["Project/Programme Name"] == "Chutney Bridge Ltd" + ) + assert master.month == "July" + assert master.quarter.quarter == 2 + assert master.quarter.end_date == datetime.date(2021, 9, 30) + + +def test_quarter_objects_have_months(): + q = Quarter(1, 2021) + assert q.months[0].start_date == datetime.date(2021, 4, 1) + + +def test_month(): + m1 = Month(1, 2021) + assert m1.month == "January" + m2 = Month(9, 2021) + assert m2.month == "September" + assert m2.start_date == datetime.date(2021, 9, 1) + assert m2.end_date == datetime.date(2021, 9, 30) + # test leap year + m3 = Month(2, 2024) + m4 = Month(2, 2028) + assert m3.end_date == datetime.date(2024, 2, 29) + assert m4.end_date == datetime.date(2028, 2, 29) + -- cgit v1.2.3