Home Assistant Unofficial Reference 2024.12.1
util.py
Go to the documentation of this file.
1 """Utility functions for Habitica."""
2 
3 from __future__ import annotations
4 
5 import datetime
6 from math import floor
7 from typing import TYPE_CHECKING, Any
8 
9 from dateutil.rrule import (
10  DAILY,
11  FR,
12  MO,
13  MONTHLY,
14  SA,
15  SU,
16  TH,
17  TU,
18  WE,
19  WEEKLY,
20  YEARLY,
21  rrule,
22 )
23 
24 from homeassistant.components.automation import automations_with_entity
25 from homeassistant.components.script import scripts_with_entity
26 from homeassistant.core import HomeAssistant
27 from homeassistant.util import dt as dt_util
28 
29 
30 def next_due_date(task: dict[str, Any], last_cron: str) -> datetime.date | None:
31  """Calculate due date for dailies and yesterdailies."""
32 
33  if task["everyX"] == 0 or not task.get("nextDue"): # grey dailies never become due
34  return None
35 
36  today = to_date(last_cron)
37  startdate = to_date(task["startDate"])
38  if TYPE_CHECKING:
39  assert today
40  assert startdate
41 
42  if task["isDue"] and not task["completed"]:
43  return to_date(last_cron)
44 
45  if startdate > today:
46  if task["frequency"] == "daily" or (
47  task["frequency"] in ("monthly", "yearly") and task["daysOfMonth"]
48  ):
49  return startdate
50 
51  if (
52  task["frequency"] in ("weekly", "monthly")
53  and (nextdue := to_date(task["nextDue"][0]))
54  and startdate > nextdue
55  ):
56  return to_date(task["nextDue"][1])
57 
58  return to_date(task["nextDue"][0])
59 
60 
61 def to_date(date: str) -> datetime.date | None:
62  """Convert an iso date to a datetime.date object."""
63  try:
64  return dt_util.as_local(datetime.datetime.fromisoformat(date)).date()
65  except ValueError:
66  # sometimes nextDue dates are JavaScript datetime strings instead of iso:
67  # "Mon May 06 2024 00:00:00 GMT+0200"
68  try:
69  return dt_util.as_local(
70  datetime.datetime.strptime(date, "%a %b %d %Y %H:%M:%S %Z%z")
71  ).date()
72  except ValueError:
73  return None
74 
75 
76 def entity_used_in(hass: HomeAssistant, entity_id: str) -> list[str]:
77  """Get list of related automations and scripts."""
78  used_in = automations_with_entity(hass, entity_id)
79  used_in += scripts_with_entity(hass, entity_id)
80  return used_in
81 
82 
83 FREQUENCY_MAP = {"daily": DAILY, "weekly": WEEKLY, "monthly": MONTHLY, "yearly": YEARLY}
84 WEEKDAY_MAP = {"m": MO, "t": TU, "w": WE, "th": TH, "f": FR, "s": SA, "su": SU}
85 
86 
87 def build_rrule(task: dict[str, Any]) -> rrule:
88  """Build rrule string."""
89 
90  rrule_frequency = FREQUENCY_MAP.get(task["frequency"], DAILY)
91  weekdays = [
92  WEEKDAY_MAP[day] for day, is_active in task["repeat"].items() if is_active
93  ]
94  bymonthday = (
95  task["daysOfMonth"]
96  if rrule_frequency == MONTHLY and task["daysOfMonth"]
97  else None
98  )
99 
100  bysetpos = None
101  if rrule_frequency == MONTHLY and task["weeksOfMonth"]:
102  bysetpos = task["weeksOfMonth"]
103  weekdays = weekdays if weekdays else [MO]
104 
105  return rrule(
106  freq=rrule_frequency,
107  interval=task["everyX"],
108  dtstart=dt_util.start_of_local_day(
109  datetime.datetime.fromisoformat(task["startDate"])
110  ),
111  byweekday=weekdays if rrule_frequency in [WEEKLY, MONTHLY] else None,
112  bymonthday=bymonthday,
113  bysetpos=bysetpos,
114  )
115 
116 
117 def get_recurrence_rule(recurrence: rrule) -> str:
118  r"""Extract and return the recurrence rule portion of an RRULE.
119 
120  This function takes an RRULE representing a task's recurrence pattern,
121  builds the RRULE string, and extracts the recurrence rule part.
122 
123  'DTSTART:YYYYMMDDTHHMMSS\nRRULE:FREQ=YEARLY;INTERVAL=2'
124 
125  Parameters
126  ----------
127  recurrence : rrule
128  An RRULE object.
129 
130  Returns
131  -------
132  str
133  The recurrence rule portion of the RRULE string, starting with 'FREQ='.
134 
135  Example
136  -------
137  >>> rule = get_recurrence_rule(task)
138  >>> print(rule)
139  'FREQ=YEARLY;INTERVAL=2'
140 
141  """
142  return str(recurrence).split("RRULE:")[1]
143 
144 
146  user: dict[str, Any], content: dict[str, Any], attribute: str
147 ) -> dict[str, float]:
148  """Get modifiers contributing to strength attribute."""
149 
150  gear_set = {
151  "weapon",
152  "armor",
153  "head",
154  "shield",
155  "back",
156  "headAccessory",
157  "eyewear",
158  "body",
159  }
160 
161  equipment = sum(
162  stats[attribute]
163  for gear in gear_set
164  if (equipped := user["items"]["gear"]["equipped"].get(gear))
165  and (stats := content["gear"]["flat"].get(equipped))
166  )
167 
168  class_bonus = sum(
169  stats[attribute] / 2
170  for gear in gear_set
171  if (equipped := user["items"]["gear"]["equipped"].get(gear))
172  and (stats := content["gear"]["flat"].get(equipped))
173  and stats["klass"] == user["stats"]["class"]
174  )
175 
176  return {
177  "level": min(floor(user["stats"]["lvl"] / 2), 50),
178  "equipment": equipment,
179  "class": class_bonus,
180  "allocated": user["stats"][attribute],
181  "buffs": user["stats"]["buffs"][attribute],
182  }
183 
184 
186  user: dict[str, Any], content: dict[str, Any], attribute: str
187 ) -> int:
188  """Get total attribute points."""
189  return floor(
190  sum(value for value in get_attribute_points(user, content, attribute).values())
191  )
list[str] automations_with_entity(HomeAssistant hass, str entity_id)
Definition: __init__.py:191
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
list[str] entity_used_in(HomeAssistant hass, str entity_id)
Definition: util.py:76
str get_recurrence_rule(rrule recurrence)
Definition: util.py:117
datetime.date|None next_due_date(dict[str, Any] task, str last_cron)
Definition: util.py:30
datetime.date|None to_date(str date)
Definition: util.py:61
rrule build_rrule(dict[str, Any] task)
Definition: util.py:87
dict[str, float] get_attribute_points(dict[str, Any] user, dict[str, Any] content, str attribute)
Definition: util.py:147
int get_attributes_total(dict[str, Any] user, dict[str, Any] content, str attribute)
Definition: util.py:187
list[str] scripts_with_entity(HomeAssistant hass, str entity_id)
Definition: __init__.py:126