home · contact · privacy
In calendar day gap filling, ensure days before and after existing days.
[plomtask] / plomtask / days.py
index 78340e2d13000e7ef49bff1bdc24f27e561993c1..e3d56d7fb1049c161b146af555b05ccf2ed2f904 100644 (file)
@@ -3,8 +3,11 @@ from __future__ import annotations
 from datetime import datetime, timedelta
 from plomtask.exceptions import BadFormatException
 from plomtask.db import DatabaseConnection, BaseModel
 from datetime import datetime, timedelta
 from plomtask.exceptions import BadFormatException
 from plomtask.db import DatabaseConnection, BaseModel
+from plomtask.todos import Todo
 
 DATE_FORMAT = '%Y-%m-%d'
 
 DATE_FORMAT = '%Y-%m-%d'
+MIN_RANGE_DATE = '2024-01-01'
+MAX_RANGE_DATE = '2030-12-31'
 
 
 def valid_date(date_str: str) -> str:
 
 
 def valid_date(date_str: str) -> str:
@@ -30,13 +33,11 @@ class Day(BaseModel[str]):
     to_save = ['comment']
 
     def __init__(self, date: str, comment: str = '') -> None:
     to_save = ['comment']
 
     def __init__(self, date: str, comment: str = '') -> None:
-        super().__init__(date)
-        self.id_: str = valid_date(date)
+        id_ = valid_date(date)
+        super().__init__(id_)
         self.datetime = datetime.strptime(self.date, DATE_FORMAT)
         self.comment = comment
         self.datetime = datetime.strptime(self.date, DATE_FORMAT)
         self.comment = comment
-
-    def __eq__(self, other: object) -> bool:
-        return isinstance(other, self.__class__) and self.date == other.date
+        self.calendarized_todos: list[Todo] = []
 
     def __lt__(self, other: Day) -> bool:
         return self.date < other.date
 
     def __lt__(self, other: Day) -> bool:
         return self.date < other.date
@@ -45,7 +46,14 @@ class Day(BaseModel[str]):
     def all(cls, db_conn: DatabaseConnection,
             date_range: tuple[str, str] = ('', ''),
             fill_gaps: bool = False) -> list[Day]:
     def all(cls, db_conn: DatabaseConnection,
             date_range: tuple[str, str] = ('', ''),
             fill_gaps: bool = False) -> list[Day]:
-        """Return list of Days in database within date_range."""
+        """Return list of Days in database within (open) date_range interval.
+
+        If no range values provided, defaults them to MIN_RANGE_DATE and
+        MAX_RANGE_DATE. Also knows to properly interpret 'today' as value.
+
+        On fill_gaps=True, will instantiate (without saving) Days of all dates
+        within the date range that don't exist yet.
+        """
         min_date = '2024-01-01'
         max_date = '2030-12-31'
         start_date = valid_date(date_range[0] if date_range[0] else min_date)
         min_date = '2024-01-01'
         max_date = '2030-12-31'
         start_date = valid_date(date_range[0] if date_range[0] else min_date)
@@ -55,22 +63,39 @@ class Day(BaseModel[str]):
         for row in db_conn.exec(sql, (start_date, end_date)):
             days += [cls.by_id(db_conn, row[0])]
         days.sort()
         for row in db_conn.exec(sql, (start_date, end_date)):
             days += [cls.by_id(db_conn, row[0])]
         days.sort()
-        if fill_gaps and len(days) > 1:
-            gapless_days = []
-            for i, day in enumerate(days):
-                gapless_days += [day]
-                if i < len(days) - 1:
-                    while day.next_date != days[i+1].date:
-                        day = Day(day.next_date)
-                        gapless_days += [day]
-            days = gapless_days
+        if fill_gaps:
+            if start_date not in [d.date for d in days]:
+                days = [Day(start_date)] + days
+            if end_date not in [d.date for d in days]:
+                days += [Day(end_date)]
+            if len(days) > 1:
+                gapless_days = []
+                for i, day in enumerate(days):
+                    gapless_days += [day]
+                    if i < len(days) - 1:
+                        while day.next_date != days[i+1].date:
+                            day = Day(day.next_date)
+                            gapless_days += [day]
+                days = gapless_days
         return days
 
     @property
     def date(self) -> str:
         """Return self.id_ under the assumption it's a date string."""
         return days
 
     @property
     def date(self) -> str:
         """Return self.id_ under the assumption it's a date string."""
+        assert isinstance(self.id_, str)
         return self.id_
 
         return self.id_
 
+    @property
+    def first_of_month(self) -> bool:
+        """Return what month self.date is part of."""
+        assert isinstance(self.id_, str)
+        return self.id_[-2:] == '01'
+
+    @property
+    def month_name(self) -> str:
+        """Return what month self.date is part of."""
+        return self.datetime.strftime('%B')
+
     @property
     def weekday(self) -> str:
         """Return what weekday matches self.date."""
     @property
     def weekday(self) -> str:
         """Return what weekday matches self.date."""
@@ -88,6 +113,7 @@ class Day(BaseModel[str]):
         next_datetime = self.datetime + timedelta(days=1)
         return next_datetime.strftime(DATE_FORMAT)
 
         next_datetime = self.datetime + timedelta(days=1)
         return next_datetime.strftime(DATE_FORMAT)
 
-    def save(self, db_conn: DatabaseConnection) -> None:
-        """Add (or re-write) self to DB and cache."""
-        self.save_core(db_conn, update_with_lastrowid=False)
+    def collect_calendarized_todos(self, db_conn: DatabaseConnection) -> None:
+        """Fill self.calendarized_todos."""
+        self.calendarized_todos = [t for t in Todo.by_date(db_conn, self.date)
+                                   if t.calendarize]