Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions custom_components/choreops/engines/schedule_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,7 @@ def calculate_next_due_date_from_chore_info(
interval_unit=custom_unit,
delta=custom_interval,
require_future=True,
reference_datetime=reference_time,
return_type=const.HELPER_RETURN_DATETIME,
),
)
Expand Down
6 changes: 6 additions & 0 deletions custom_components/choreops/managers/chore_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5679,8 +5679,14 @@ def _record_chore_missed(
# Emit signal for StatisticsManager to handle period buckets
# Pass missed_streak_tally for daily bucket snapshot (display purposes)
# Phase 3 Step 2: Include optional due_date and reason fields (D-07)
chore_info = self.coordinator.chores_data.get(chore_id)
signal_payload: dict[str, Any] = {
"chore_id": chore_id,
"chore_name": str(
chore_info.get(const.DATA_CHORE_NAME, "Unknown Chore")
if chore_info
else "Unknown Chore"
),
"user_id": assignee_id,
"user_name": assignee_name,
"missed_streak_tally": new_missed_streak,
Expand Down
28 changes: 23 additions & 5 deletions custom_components/choreops/managers/notification_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,22 @@ def _translate_action_buttons(

return translated_actions

def _get_points_label(self) -> str:
"""Return the configured label for points."""
return str(
self.coordinator.config_entry.options.get(
const.CONF_POINTS_LABEL, const.DEFAULT_POINTS_LABEL
)
)

def _build_notification_placeholders(
self, message_data: dict[str, Any] | None
) -> dict[str, Any]:
"""Build notification placeholders with shared defaults."""
placeholders = dict(message_data or {})
placeholders.setdefault("points_label", self._get_points_label())
return placeholders

# =========================================================================
# Core Notification Send Method
# =========================================================================
Expand Down Expand Up @@ -1073,17 +1089,18 @@ async def notify_assignee_translated(
message_json_key = self._convert_notification_key(message_key)
title_notification = translations.get(title_json_key, {})
message_notification = translations.get(message_json_key, {})
placeholders = self._build_notification_placeholders(message_data)

# Format title and message with placeholders
title = self._format_notification_text(
title_notification.get("title", title_key),
message_data,
placeholders,
title_json_key,
"title",
)
message = self._format_notification_text(
message_notification.get("message", message_key),
message_data,
placeholders,
message_json_key,
"message",
)
Expand Down Expand Up @@ -1262,17 +1279,18 @@ async def notify_approvers_translated(
message_json_key = self._convert_notification_key(message_key)
title_notification = translations.get(title_json_key, {})
message_notification = translations.get(message_json_key, {})
placeholders = self._build_notification_placeholders(message_data)

# Format both title and message with placeholders
title = self._format_notification_text(
title_notification.get("title", title_key),
message_data,
placeholders,
title_json_key,
"title",
)
message = self._format_notification_text(
message_notification.get("message", message_key),
message_data,
placeholders,
message_json_key,
"message",
)
Expand Down Expand Up @@ -1384,7 +1402,7 @@ async def broadcast_to_all_approvers(
"""
perf_start = time.perf_counter()
notification_tasks: list[tuple[str, Any]] = []
message_data = placeholders or {}
message_data = self._build_notification_placeholders(placeholders)

for approver_id, approver_info in self.coordinator.approvers_data.items():
# Use approver's language preference, fall back to system language
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@
"_comment_assignee_footer": "══════════════════════════════════════════════════════════════════",
"chore_approved_assignee": {
"title": "🎉 Chore Approved!",
"message": "{chore_name} approved! You earned {points} points"
"message": "{chore_name} approved! You earned {points} {points_label}"
},
"chore_disapproved_assignee": {
"title": "↩️ Chore Returned",
"message": "{chore_name} needs more work."
},
"chore_overdue_assignee": {
"title": "⏰ Chore Overdue",
"message": "{chore_name} is overdue! Complete it now to earn {points} points"
"message": "{chore_name} is overdue! Complete it now to earn {points} {points_label}"
},
"chore_overdue_steal_available": {
"title": "⚡ Chore Alert",
"message": "{chore_name} is overdue and available to steal! Complete it first to earn {points} points"
"message": "{chore_name} is overdue and available to steal! Complete it first to earn {points} {points_label}"
},
"chore_missed_assignee": {
"title": "😔 Chore Expired",
"message": "{chore_name} expired without completion."
},
"chore_due_reminder_assignee": {
"title": "⏰ Chore Reminder",
"message": "{chore_name} is due in {minutes} minutes! Worth {points} points"
"message": "{chore_name} is due in {minutes} minutes! Worth {points} {points_label}"
},
"chore_due_window_assignee": {
"title": "🎯 Chore Now Due",
"message": "{chore_name} is now due! Complete within {hours} hour(s) to earn {points} points"
"message": "{chore_name} is now due! Complete within {hours} hour(s) to earn {points} {points_label}"
},
"reward_approved_assignee": {
"title": "🎉 Reward Approved!",
Expand All @@ -52,15 +52,15 @@
},
"penalty_applied_assignee": {
"title": "⚠️ Penalty Applied",
"message": "{penalty_name}: {points} points deducted. Let's get back on track!"
"message": "{penalty_name}: {points} {points_label} deducted. Let's get back on track!"
},
"bonus_applied_assignee": {
"title": "⭐ Bonus Applied!",
"message": "{bonus_name}: You earned {points} bonus points! Great job"
"message": "{bonus_name}: You earned {points} bonus {points_label}! Great job"
},
"multiplier_changed_assignee": {
"title": "⚖️ Multiplier Updated",
"message": "Your points multiplier changed from {old_multiplier}x to {new_multiplier}x"
"message": "Your {points_label} multiplier changed from {old_multiplier}x to {new_multiplier}x"
},
"_comment_approver_section": "══════════════════════════════════════════════════════════════════",
"_comment_approver_desc": "APPROVER NOTIFICATIONS: Informative, third-person, actionable. Format: '{assignee_name}: Action'. NO 'ChoreOps:' prefix.",
Expand All @@ -79,11 +79,11 @@
},
"pending_chores_approver": {
"title": "📋 {assignee_name}: {count} Pending",
"message": "{count} chores awaiting review. Latest: {latest_chore} (+{points} pts)"
"message": "{count} chores awaiting review. Latest: {latest_chore} (+{points} {points_label})"
},
"reward_claimed_approver": {
"title": "🎁 {assignee_name}: Reward Request",
"message": "{assignee_name} requested {reward_name} for {points} points"
"message": "{assignee_name} requested {reward_name} for {points} {points_label}"
},
"reward_reminder_approver": {
"title": "⏰ {assignee_name}: Reward Pending",
Expand All @@ -103,7 +103,7 @@
},
"multiplier_changed_approver": {
"title": "⚖️ {assignee_name}: Multiplier Updated",
"message": "{assignee_name}'s points multiplier changed from {old_multiplier}x to {new_multiplier}x"
"message": "{assignee_name}'s {points_label} multiplier changed from {old_multiplier}x to {new_multiplier}x"
},
"_comment_system_section": "══════════════════════════════════════════════════════════════════",
"_comment_system_desc": "SYSTEM/ADMIN NOTIFICATIONS: Data management operations",
Expand Down
Loading