251 files
This commit is contained in:
@@ -20,6 +20,84 @@ _LOGGER = logging.getLogger(__name__)
|
||||
class PointsMixin:
|
||||
"""Mixin providing points, streaks, penalties, bonuses, and notifications."""
|
||||
|
||||
def week_progress(self, children, chores, completions) -> dict[str, dict[str, int]]:
|
||||
"""Points each child has earned this week, and the most they could have.
|
||||
|
||||
The week runs Monday to Sunday, like the weekly digest. ``earned`` is
|
||||
the approved chore points since Monday (bonus sub-tasks excluded, as
|
||||
they are extras on top of the jobs). ``available`` is every scheduled
|
||||
job's points at its daily limit across all seven days, with the weekend
|
||||
multiplier on Saturday and Sunday and the family's planned vacation days
|
||||
left out (a child's live away-sensor is not used: it says nothing about
|
||||
the rest of the week). Rotation
|
||||
chores are skipped — who is active on a future day can't be known — so
|
||||
``earned`` may run past ``available``; cards cap the display.
|
||||
|
||||
A weekly-target chore (#883) is picked up on whichever days the child
|
||||
chooses, so it adds its quota (target × points) once rather than a slot
|
||||
per scheduled day. An open-ended chore has no price of its own, so it
|
||||
adds nothing to ``available`` (what was paid for it still counts in
|
||||
``earned``). A teamwork chore counts each member's share, as paid.
|
||||
"""
|
||||
today = dt_util.now().date()
|
||||
week_start = today - timedelta(days=today.weekday())
|
||||
days = [week_start + timedelta(days=i) for i in range(7)]
|
||||
try:
|
||||
multiplier = float(self.storage.get_setting("weekend_multiplier", "2.0"))
|
||||
except (ValueError, TypeError):
|
||||
multiplier = 2.0
|
||||
|
||||
earned: dict[str, int] = {}
|
||||
for comp in completions:
|
||||
if not comp.approved or comp.bonus_subtask_id:
|
||||
continue
|
||||
if dt_util.as_local(comp.completed_at).date() < week_start:
|
||||
continue
|
||||
earned[comp.child_id] = earned.get(comp.child_id, 0) + (comp.points_awarded or 0)
|
||||
|
||||
celebration_multiplier = self.birthday_multiplier()
|
||||
|
||||
progress: dict[str, dict[str, int]] = {}
|
||||
for child in children:
|
||||
available = 0
|
||||
for chore in chores:
|
||||
if not getattr(chore, "enabled", True):
|
||||
continue
|
||||
assigned = chore.assigned_to or []
|
||||
if assigned and child.id not in assigned:
|
||||
continue
|
||||
if child.id in (getattr(chore, "disabled_for", None) or []):
|
||||
continue
|
||||
mode = getattr(chore, "assignment_mode", "everyone") or "everyone"
|
||||
if mode not in ("everyone", "first_come"):
|
||||
continue
|
||||
if getattr(chore, "open_ended", False):
|
||||
continue
|
||||
limit = 1 if mode == "first_come" else max(1, int(getattr(chore, "daily_limit", 1) or 1))
|
||||
# A teamwork chore (#928) pays each member their share (plus any
|
||||
# team bonus), so that is what it can add to a child's week.
|
||||
points = self.team_share_points(chore)
|
||||
weekly_target = int(getattr(chore, "weekly_target", 0) or 0)
|
||||
if weekly_target:
|
||||
available += weekly_target * points
|
||||
continue
|
||||
for day in days:
|
||||
if self.is_vacation_day(day) or not self._is_chore_scheduled_for_date(chore, day):
|
||||
continue
|
||||
# A birthday off (#924) only excuses non-mandatory chores.
|
||||
birthday = self.is_birthday(child, day)
|
||||
if birthday and not getattr(chore, "mandatory", False) and self.birthday_chores_off():
|
||||
continue
|
||||
per = points
|
||||
if day.weekday() in (5, 6) and multiplier > 1.0:
|
||||
per += round(points * (multiplier - 1.0))
|
||||
# The birthday multiplier stacks on the weekend one, as paid.
|
||||
if birthday and celebration_multiplier > 1.0:
|
||||
per += round(per * (celebration_multiplier - 1.0))
|
||||
available += per * limit
|
||||
progress[child.id] = {"earned": earned.get(child.id, 0), "available": available}
|
||||
return progress
|
||||
|
||||
async def _async_check_perfect_week(self) -> None:
|
||||
"""Award perfect week bonus to children who completed at least one chore every day last week."""
|
||||
perfect_week_enabled = self.storage.get_setting("perfect_week_enabled", "true") == "true"
|
||||
|
||||
Reference in New Issue
Block a user