Refactor calendars in Habitica (#131020)

* Refactor calendars

* changes
This commit is contained in:
Manu 2024-11-29 03:31:38 +01:00 committed by GitHub
parent 5c8fb5ec2c
commit 8e12fbff88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from abc import abstractmethod
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from enum import StrEnum from enum import StrEnum
@ -60,6 +61,43 @@ class HabiticaCalendarEntity(HabiticaBase, CalendarEntity):
"""Initialize calendar entity.""" """Initialize calendar entity."""
super().__init__(coordinator, self.entity_description) super().__init__(coordinator, self.entity_description)
@abstractmethod
def get_events(
self, start_date: datetime, end_date: datetime | None = None
) -> list[CalendarEvent]:
"""Return events."""
@property
def event(self) -> CalendarEvent | None:
"""Return the current or next upcoming event."""
return next(iter(self.get_events(dt_util.now())), None)
async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
) -> list[CalendarEvent]:
"""Return calendar events within a datetime range."""
return self.get_events(start_date, end_date)
@property
def start_of_today(self) -> datetime:
"""Habitica daystart."""
return dt_util.start_of_local_day(
datetime.fromisoformat(self.coordinator.data.user["lastCron"])
)
def get_recurrence_dates(
self, recurrences: rrule, start_date: datetime, end_date: datetime | None = None
) -> list[datetime]:
"""Calculate recurrence dates based on start_date and end_date."""
if end_date:
return recurrences.between(
start_date, end_date - timedelta(days=1), inc=True
)
# if no end_date is given, return only the next recurrence
return [recurrences.after(start_date, inc=True)]
class HabiticaTodosCalendarEntity(HabiticaCalendarEntity): class HabiticaTodosCalendarEntity(HabiticaCalendarEntity):
"""Habitica todos calendar entity.""" """Habitica todos calendar entity."""
@ -69,7 +107,7 @@ class HabiticaTodosCalendarEntity(HabiticaCalendarEntity):
translation_key=HabiticaCalendar.TODOS, translation_key=HabiticaCalendar.TODOS,
) )
def dated_todos( def get_events(
self, start_date: datetime, end_date: datetime | None = None self, start_date: datetime, end_date: datetime | None = None
) -> list[CalendarEvent]: ) -> list[CalendarEvent]:
"""Get all dated todos.""" """Get all dated todos."""
@ -112,18 +150,6 @@ class HabiticaTodosCalendarEntity(HabiticaCalendarEntity):
), ),
) )
@property
def event(self) -> CalendarEvent | None:
"""Return the current or next upcoming event."""
return next(iter(self.dated_todos(dt_util.now())), None)
async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
) -> list[CalendarEvent]:
"""Return calendar events within a datetime range."""
return self.dated_todos(start_date, end_date)
class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity): class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
"""Habitica dailies calendar entity.""" """Habitica dailies calendar entity."""
@ -133,13 +159,6 @@ class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
translation_key=HabiticaCalendar.DAILIES, translation_key=HabiticaCalendar.DAILIES,
) )
@property
def today(self) -> datetime:
"""Habitica daystart."""
return dt_util.start_of_local_day(
datetime.fromisoformat(self.coordinator.data.user["lastCron"])
)
def end_date(self, recurrence: datetime, end: datetime | None = None) -> date: def end_date(self, recurrence: datetime, end: datetime | None = None) -> date:
"""Calculate the end date for a yesterdaily. """Calculate the end date for a yesterdaily.
@ -152,29 +171,20 @@ class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
if end: if end:
return recurrence.date() + timedelta(days=1) return recurrence.date() + timedelta(days=1)
return ( return (
dt_util.start_of_local_day() if recurrence == self.today else recurrence dt_util.start_of_local_day()
if recurrence == self.start_of_today
else recurrence
).date() + timedelta(days=1) ).date() + timedelta(days=1)
def get_recurrence_dates( def get_events(
self, recurrences: rrule, start_date: datetime, end_date: datetime | None = None
) -> list[datetime]:
"""Calculate recurrence dates based on start_date and end_date."""
if end_date:
return recurrences.between(
start_date, end_date - timedelta(days=1), inc=True
)
# if no end_date is given, return only the next recurrence
return [recurrences.after(self.today, inc=True)]
def due_dailies(
self, start_date: datetime, end_date: datetime | None = None self, start_date: datetime, end_date: datetime | None = None
) -> list[CalendarEvent]: ) -> list[CalendarEvent]:
"""Get dailies and recurrences for a given period or the next upcoming.""" """Get dailies and recurrences for a given period or the next upcoming."""
# we only have dailies for today and future recurrences # we only have dailies for today and future recurrences
if end_date and end_date < self.today: if end_date and end_date < self.start_of_today:
return [] return []
start_date = max(start_date, self.today) start_date = max(start_date, self.start_of_today)
events = [] events = []
for task in self.coordinator.data.tasks: for task in self.coordinator.data.tasks:
@ -187,10 +197,12 @@ class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
recurrences, start_date, end_date recurrences, start_date, end_date
) )
for recurrence in recurrence_dates: for recurrence in recurrence_dates:
is_future_event = recurrence > self.today is_future_event = recurrence > self.start_of_today
is_current_event = recurrence <= self.today and not task["completed"] is_current_event = (
recurrence <= self.start_of_today and not task["completed"]
)
if not (is_future_event or is_current_event): if not is_future_event and not is_current_event:
continue continue
events.append( events.append(
@ -214,20 +226,15 @@ class HabiticaDailiesCalendarEntity(HabiticaCalendarEntity):
@property @property
def event(self) -> CalendarEvent | None: def event(self) -> CalendarEvent | None:
"""Return the next upcoming event.""" """Return the next upcoming event."""
return next(iter(self.due_dailies(self.today)), None) return next(iter(self.get_events(self.start_of_today)), None)
async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
) -> list[CalendarEvent]:
"""Return calendar events within a datetime range."""
return self.due_dailies(start_date, end_date)
@property @property
def extra_state_attributes(self) -> dict[str, bool | None] | None: def extra_state_attributes(self) -> dict[str, bool | None] | None:
"""Return entity specific state attributes.""" """Return entity specific state attributes."""
return { return {
"yesterdaily": self.event.start < self.today.date() if self.event else None "yesterdaily": self.event.start < self.start_of_today.date()
if self.event
else None
} }
@ -239,7 +246,7 @@ class HabiticaTodoRemindersCalendarEntity(HabiticaCalendarEntity):
translation_key=HabiticaCalendar.TODO_REMINDERS, translation_key=HabiticaCalendar.TODO_REMINDERS,
) )
def reminders( def get_events(
self, start_date: datetime, end_date: datetime | None = None self, start_date: datetime, end_date: datetime | None = None
) -> list[CalendarEvent]: ) -> list[CalendarEvent]:
"""Reminders for todos.""" """Reminders for todos."""
@ -282,18 +289,6 @@ class HabiticaTodoRemindersCalendarEntity(HabiticaCalendarEntity):
key=lambda event: event.start, key=lambda event: event.start,
) )
@property
def event(self) -> CalendarEvent | None:
"""Return the next upcoming event."""
return next(iter(self.reminders(dt_util.now())), None)
async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
) -> list[CalendarEvent]:
"""Return calendar events within a datetime range."""
return self.reminders(start_date, end_date)
class HabiticaDailyRemindersCalendarEntity(HabiticaCalendarEntity): class HabiticaDailyRemindersCalendarEntity(HabiticaCalendarEntity):
"""Habitica daily reminders calendar entity.""" """Habitica daily reminders calendar entity."""
@ -321,47 +316,31 @@ class HabiticaDailyRemindersCalendarEntity(HabiticaCalendarEntity):
tzinfo=dt_util.DEFAULT_TIME_ZONE, tzinfo=dt_util.DEFAULT_TIME_ZONE,
) )
@property def get_events(
def today(self) -> datetime:
"""Habitica daystart."""
return dt_util.start_of_local_day(
datetime.fromisoformat(self.coordinator.data.user["lastCron"])
)
def get_recurrence_dates(
self, recurrences: rrule, start_date: datetime, end_date: datetime | None = None
) -> list[datetime]:
"""Calculate recurrence dates based on start_date and end_date."""
if end_date:
return recurrences.between(
start_date, end_date - timedelta(days=1), inc=True
)
# if no end_date is given, return only the next recurrence
return [recurrences.after(self.today, inc=True)]
def reminders(
self, start_date: datetime, end_date: datetime | None = None self, start_date: datetime, end_date: datetime | None = None
) -> list[CalendarEvent]: ) -> list[CalendarEvent]:
"""Reminders for dailies.""" """Reminders for dailies."""
events = [] events = []
if end_date and end_date < self.today: if end_date and end_date < self.start_of_today:
return [] return []
start_date = max(start_date, self.today) start_date = max(start_date, self.start_of_today)
for task in self.coordinator.data.tasks: for task in self.coordinator.data.tasks:
if not (task["type"] == HabiticaTaskType.DAILY and task["everyX"]): if not (task["type"] == HabiticaTaskType.DAILY and task["everyX"]):
continue continue
recurrences = build_rrule(task) recurrences = build_rrule(task)
recurrences_start = self.today recurrences_start = self.start_of_today
recurrence_dates = self.get_recurrence_dates( recurrence_dates = self.get_recurrence_dates(
recurrences, recurrences_start, end_date recurrences, recurrences_start, end_date
) )
for recurrence in recurrence_dates: for recurrence in recurrence_dates:
is_future_event = recurrence > self.today is_future_event = recurrence > self.start_of_today
is_current_event = recurrence <= self.today and not task["completed"] is_current_event = (
recurrence <= self.start_of_today and not task["completed"]
)
if not is_future_event and not is_current_event: if not is_future_event and not is_current_event:
continue continue
@ -388,15 +367,3 @@ class HabiticaDailyRemindersCalendarEntity(HabiticaCalendarEntity):
events, events,
key=lambda event: event.start, key=lambda event: event.start,
) )
@property
def event(self) -> CalendarEvent | None:
"""Return the next upcoming event."""
return next(iter(self.reminders(dt_util.now())), None)
async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
) -> list[CalendarEvent]:
"""Return calendar events within a datetime range."""
return self.reminders(start_date, end_date)