From eff89a3ebc0b3bf5b340b0ebd2b32fa136d8f640 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Thu, 30 May 2024 08:57:46 +0200 Subject: [PATCH] Re-factor date ranging and default to range 'yesterday' to 'tomorrow'. --- plomtask/dating.py | 2 -- plomtask/days.py | 48 ++++++++++++++++++++++++---------------------- plomtask/db.py | 16 ++++++++-------- plomtask/http.py | 14 ++++++++------ tests/days.py | 24 +++++++++++------------ 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/plomtask/dating.py b/plomtask/dating.py index 711da95..c55d847 100644 --- a/plomtask/dating.py +++ b/plomtask/dating.py @@ -3,8 +3,6 @@ from datetime import datetime, timedelta from plomtask.exceptions import BadFormatException DATE_FORMAT = '%Y-%m-%d' -MIN_RANGE_DATE = '2024-01-01' -MAX_RANGE_DATE = '2030-12-31' def valid_date(date_str: str) -> str: diff --git a/plomtask/days.py b/plomtask/days.py index d7083b4..0815b9b 100644 --- a/plomtask/days.py +++ b/plomtask/days.py @@ -22,31 +22,33 @@ class Day(BaseModel[str]): return self.date < other.date @classmethod - def all(cls, db_conn: DatabaseConnection, - date_range: tuple[str, str] = ('', ''), - fill_gaps: bool = False) -> list[Day]: - """Return list of Days in database within (open) date_range interval. - - On fill_gaps=True, will instantiate (without saving) Days of all dates - within the date range that don't exist yet. - """ - ret = cls.by_date_range_with_limits(db_conn, date_range, 'id') + def by_date_range_filled(cls, db_conn: DatabaseConnection, + start: str, end: str) -> list[Day]: + """Return days existing and non-existing between dates start/end.""" + ret = cls.by_date_range_with_limits(db_conn, (start, end), 'id') days, start_date, end_date = ret + return cls.with_filled_gaps(days, start_date, end_date) + + @classmethod + def with_filled_gaps(cls, days: list[Day], start_date: str, end_date: str + ) -> list[Day]: + """In days, fill with (un-saved) Days gaps between start/end_date.""" + if start_date > end_date: + return days days.sort() - 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 + 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 diff --git a/plomtask/db.py b/plomtask/db.py index 4396b44..b5461a5 100644 --- a/plomtask/db.py +++ b/plomtask/db.py @@ -6,7 +6,7 @@ from difflib import Differ from sqlite3 import connect as sql_connect, Cursor, Row from typing import Any, Self, TypeVar, Generic from plomtask.exceptions import HandledException, NotFoundException -from plomtask.dating import (MIN_RANGE_DATE, MAX_RANGE_DATE, valid_date) +from plomtask.dating import valid_date EXPECTED_DB_VERSION = 4 MIGRATIONS_DIR = 'migrations' @@ -358,19 +358,19 @@ class BaseModel(Generic[BaseModelId]): @classmethod def by_date_range_with_limits(cls: type[BaseModelInstance], db_conn: DatabaseConnection, - date_range: tuple[str, str] = ('', ''), + date_range: tuple[str, str], date_col: str = 'day' ) -> tuple[list[BaseModelInstance], str, str]: """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. + If no range values provided, defaults them to 'yesterday' and + 'tomorrow'. Knows to properly interpret these and 'today' as value. """ - min_date = MIN_RANGE_DATE - max_date = MAX_RANGE_DATE - start_date = valid_date(date_range[0] if date_range[0] else min_date) - end_date = valid_date(date_range[1] if date_range[1] else max_date) + start_str = date_range[0] if date_range[0] else 'yesterday' + end_str = date_range[1] if date_range[1] else 'tomorrow' + start_date = valid_date(start_str) + end_date = valid_date(end_str) items = [] sql = f'SELECT id FROM {cls.table_name} ' sql += f'WHERE {date_col} >= ? AND {date_col} <= ?' diff --git a/plomtask/http.py b/plomtask/http.py index a2e8fa6..cf7bb08 100644 --- a/plomtask/http.py +++ b/plomtask/http.py @@ -113,7 +113,9 @@ class TaskHandler(BaseHTTPRequestHandler): """Show Days from ?start= to ?end=.""" start = self.params.get_str('start') end = self.params.get_str('end') - days = Day.all(self.conn, date_range=(start, end), fill_gaps=True) + ret = Day.by_date_range_with_limits(self.conn, (start, end), 'id') + days, start, end = ret + days = Day.with_filled_gaps(days, start, end) for day in days: day.collect_calendarized_todos(self.conn) return {'start': start, 'end': end, 'days': days} @@ -161,11 +163,11 @@ class TaskHandler(BaseHTTPRequestHandler): process_id = self.params.get_int_or_none('process_id') comment_pattern = self.params.get_str('comment_pattern') todos = [] - for t in Todo.by_date_range(self.conn, (start, end)): - if (process_id and t.process.id_ != process_id)\ - or (comment_pattern not in t.comment): - continue - todos += [t] + ret = Todo.by_date_range_with_limits(self.conn, (start, end)) + todos_by_date_range, start, end = ret + todos = [t for t in todos_by_date_range + if comment_pattern in t.comment + and ((not process_id) or t.process.id_ == process_id)] if sort_by == 'doneness': todos.sort(key=lambda t: t.is_done) elif sort_by == '-doneness': diff --git a/tests/days.py b/tests/days.py index c1e1343..4727fac 100644 --- a/tests/days.py +++ b/tests/days.py @@ -60,25 +60,21 @@ class TestsWithDB(TestCaseWithDB): """Test .by_id().""" self.check_by_id() - def test_Day_all(self) -> None: - """Test Day.all(), especially in regards to date range filtering.""" + def test_Day_by_date_range_filled(self) -> None: + """Test Day.by_date_range_filled.""" date1, date2, date3 = self.default_ids day1, day2, day3 = self.check_all() - self.assertEqual(Day.all(self.db_conn, ('', '')), - [day1, day2, day3]) # check date range is a closed interval - self.assertEqual(Day.all(self.db_conn, (date1, date3)), + self.assertEqual(Day.by_date_range_filled(self.db_conn, date1, date3), [day1, day2, day3]) # check first date range value excludes what's earlier - self.assertEqual(Day.all(self.db_conn, (date2, date3)), + self.assertEqual(Day.by_date_range_filled(self.db_conn, date2, date3), [day2, day3]) - self.assertEqual(Day.all(self.db_conn, (date3, '')), - [day3]) # check second date range value excludes what's later - self.assertEqual(Day.all(self.db_conn, ('', date2)), + self.assertEqual(Day.by_date_range_filled(self.db_conn, date1, date2), [day1, day2]) # check swapped (impossible) date range returns emptiness - self.assertEqual(Day.all(self.db_conn, (date3, date1)), + self.assertEqual(Day.by_date_range_filled(self.db_conn, date3, date1), []) # check fill_gaps= instantiates unsaved dates within date range # (but does not store them) @@ -86,14 +82,16 @@ class TestsWithDB(TestCaseWithDB): day6 = Day('2024-01-06') day6.save(self.db_conn) day7 = Day('2024-01-07') - self.assertEqual(Day.all(self.db_conn, (day5.date, day7.date), - fill_gaps=True), + self.assertEqual(Day.by_date_range_filled(self.db_conn, + day5.date, day7.date), [day5, day6, day7]) self.check_storage([day1, day2, day3, day6]) # check 'today' is interpreted as today's date today = Day(todays_date()) today.save(self.db_conn) - self.assertEqual(Day.all(self.db_conn, ('today', 'today')), [today]) + self.assertEqual(Day.by_date_range_filled(self.db_conn, + 'today', 'today'), + [today]) def test_Day_remove(self) -> None: """Test .remove() effects on DB and cache.""" -- 2.30.2