Skip to content

Commit 7c72f6c

Browse files
committed
Add new functionality to format the timestamps based on the timezone provided or system timezone.
#2
1 parent f68e465 commit 7c72f6c

File tree

3 files changed

+487
-15
lines changed

3 files changed

+487
-15
lines changed

tools/developer_tools/bely-mqtt-message-broker/examples/handlers/apprise_smart_notification/formatters.py

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"""
44

55
import logging
6+
from datetime import datetime
67
from typing import Optional, Union
8+
from zoneinfo import ZoneInfo
79

810
from bely_mqtt import (
911
LogEntryAddEvent,
@@ -23,23 +25,64 @@
2325
class NotificationFormatter:
2426
"""Handles formatting of notification messages."""
2527

26-
def __init__(self, bely_url: Optional[str], logger: logging.Logger):
28+
def __init__(
29+
self, bely_url: Optional[str], logger: logging.Logger, timezone: Optional[str] = None
30+
):
2731
"""
2832
Initialize the formatter.
2933
3034
Args:
3135
bely_url: Base URL for BELY instance
3236
logger: Logger instance for output
37+
timezone: Timezone string (e.g., 'America/New_York'). If None, uses system local timezone.
3338
"""
3439
self.bely_url = bely_url
3540
self.logger = logger
3641

42+
# Set timezone - use provided timezone, or try to detect local timezone
43+
if timezone:
44+
try:
45+
self.timezone = ZoneInfo(timezone)
46+
except Exception as e:
47+
self.logger.warning(f"Invalid timezone '{timezone}': {e}. Using UTC.")
48+
self.timezone = ZoneInfo("UTC")
49+
else:
50+
# Try to detect local timezone
51+
try:
52+
import tzlocal
53+
54+
self.timezone = tzlocal.get_localzone()
55+
except (ImportError, Exception) as e:
56+
self.logger.debug(f"Could not detect local timezone: {e}. Using UTC.")
57+
self.timezone = ZoneInfo("UTC")
58+
59+
def _format_timestamp(self, timestamp: datetime) -> str:
60+
"""
61+
Format a timestamp for display in notifications.
62+
63+
Args:
64+
timestamp: The datetime object to format
65+
66+
Returns:
67+
Formatted timestamp string in local timezone
68+
"""
69+
# Ensure timestamp is timezone-aware
70+
if timestamp.tzinfo is None:
71+
# Assume UTC if no timezone info
72+
timestamp = timestamp.replace(tzinfo=ZoneInfo("UTC"))
73+
74+
# Convert to local timezone
75+
local_timestamp = timestamp.astimezone(self.timezone)
76+
77+
# Format as readable string with timezone
78+
return local_timestamp.strftime("%Y-%m-%d %H:%M:%S %Z")
79+
3780
def format_entry_added(self, event: LogEntryAddEvent) -> str:
3881
"""Format notification body for new log entry."""
3982
body = (
4083
f"New entry added to {event.parent_log_document_info.name}<br/>"
4184
f"By: {event.event_triggered_by_username}<br/>"
42-
f"Time: {event.event_timestamp}<br/>"
85+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
4386
f"Description: {event.description}<br/>"
4487
f"<br/>Entry markdown: {self._format_text_diff_pre(event.text_diff)}"
4588
)
@@ -51,7 +94,7 @@ def format_entry_updated(self, event: LogEntryUpdateEvent) -> str:
5194
f"Entry updated in {event.parent_log_document_info.name}<br/>"
5295
f"Updated by: {event.event_triggered_by_username}<br/>"
5396
f"Original author: {event.log_info.entered_by_username}<br/>"
54-
f"Time: {event.event_timestamp}<br/>"
97+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
5598
f"Description: {event.description}<br/>"
5699
f"<br/>Entry markdown changes: {self._format_text_diff_pre(event.text_diff)}"
57100
)
@@ -63,7 +106,7 @@ def format_own_entry_edited(self, event: LogEntryUpdateEvent) -> str:
63106
f"Entry edited in {event.parent_log_document_info.name}<br/>"
64107
f"Original author: {event.log_info.entered_by_username}<br/>"
65108
f"Edited by: {event.event_triggered_by_username}<br/>"
66-
f"Time: {event.event_timestamp}<br/>"
109+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
67110
f"Description: {event.description}<br/>"
68111
f"<br/>Entry markdown changes: {self._format_text_diff_pre(event.text_diff)}"
69112
)
@@ -75,7 +118,7 @@ def format_reply_added(self, event: LogEntryReplyAddEvent) -> str:
75118
f"New reply to entry in {event.parent_log_document_info.name}<br/>"
76119
f"Entry by: {event.parent_log_info.entered_by_username}<br/>"
77120
f"Reply by: {event.event_triggered_by_username}<br/>"
78-
f"Time: {event.event_timestamp}<br/>"
121+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
79122
f"<br/>Reply markdown: {self._format_text_diff_pre(event.text_diff)}"
80123
)
81124
return self._append_permalink_and_trigger(body, event)
@@ -86,7 +129,7 @@ def format_reply_updated(self, event: LogEntryReplyUpdateEvent) -> str:
86129
f"Reply updated in {event.parent_log_document_info.name}<br/>"
87130
f"Updated by: {event.event_triggered_by_username}<br/>"
88131
f"On entry by: {event.parent_log_info.entered_by_username}<br/>"
89-
f"Time: {event.event_timestamp}<br/>"
132+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
90133
f"<br/>Reply markdown changes: {self._format_text_diff_pre(event.text_diff)}"
91134
)
92135
return self._append_permalink_and_trigger(body, event)
@@ -97,7 +140,7 @@ def format_own_reply_updated(self, event: LogEntryReplyUpdateEvent) -> str:
97140
f"Reply updated on entry in {event.parent_log_document_info.name}<br/>"
98141
f"Entry by: {event.parent_log_info.entered_by_username}<br/>"
99142
f"Updated by: {event.event_triggered_by_username}<br/>"
100-
f"Time: {event.event_timestamp}<br/>"
143+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
101144
f"<br/>Reply markdown changes: {self._format_text_diff_pre(event.text_diff)}"
102145
)
103146
return self._append_permalink_and_trigger(body, event, "own_reply_update")
@@ -108,7 +151,7 @@ def format_document_reply(self, event: LogEntryReplyAddEvent) -> str:
108151
f"New reply added in document {event.parent_log_document_info.name}<br/>"
109152
f"Reply by: {event.event_triggered_by_username}<br/>"
110153
f"To entry by: {event.parent_log_info.entered_by_username}<br/>"
111-
f"Time: {event.event_timestamp}<br/>"
154+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
112155
f"<br/>Reply markdown: {self._format_text_diff_pre(event.text_diff)}"
113156
)
114157
return self._append_permalink_and_trigger(body, event, "document_owner")
@@ -119,7 +162,7 @@ def format_reaction_added(self, event: LogReactionAddEvent) -> str:
119162
body = (
120163
f"New reaction added to entry in {event.parent_log_document_info.name}<br/>"
121164
f"By: {event.event_triggered_by_username}<br/>"
122-
f"Time: {event.event_timestamp}<br/>"
165+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
123166
f"Reaction: {reaction_info.emoji} {reaction_info.name}<br/>"
124167
f"Description: {event.description}"
125168
)
@@ -131,7 +174,7 @@ def format_reaction_deleted(self, event: LogReactionDeleteEvent) -> str:
131174
body = (
132175
f"Reaction removed from entry in {event.parent_log_document_info.name}<br/>"
133176
f"By: {event.event_triggered_by_username}<br/>"
134-
f"Time: {event.event_timestamp}<br/>"
177+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
135178
f"Reaction: {reaction_info.emoji} {reaction_info.name}<br/>"
136179
f"Description: {event.description}"
137180
)
@@ -143,7 +186,7 @@ def format_entry_deleted(self, event: LogEntryDeleteEvent) -> str:
143186
f"Entry deleted from {event.parent_log_document_info.name}<br/>"
144187
f"Deleted by: {event.event_triggered_by_username}<br/>"
145188
f"Original author: {event.log_info.entered_by_username}<br/>"
146-
f"Time: {event.event_timestamp}<br/>"
189+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
147190
f"Description: {event.description}<br/>"
148191
f"<br/>Deleted entry content: {self._format_text_diff_pre(event.text_diff)}"
149192
)
@@ -154,7 +197,7 @@ def format_own_entry_deleted(self, event: LogEntryDeleteEvent) -> str:
154197
body = (
155198
f"Entry was deleted from {event.parent_log_document_info.name}<br/>"
156199
f"Deleted by: {event.event_triggered_by_username}<br/>"
157-
f"Time: {event.event_timestamp}<br/>"
200+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
158201
f"Description: {event.description}<br/>"
159202
f"<br/>Deleted entry content: {self._format_text_diff_pre(event.text_diff)}"
160203
)
@@ -165,7 +208,7 @@ def format_reply_deleted(self, event: LogEntryReplyDeleteEvent) -> str:
165208
body = (
166209
f"Reply deleted from entry in {event.parent_log_document_info.name}<br/>"
167210
f"Deleted by: {event.event_triggered_by_username}<br/>"
168-
f"Time: {event.event_timestamp}<br/>"
211+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
169212
f"<br/>Deleted reply content: {self._format_text_diff_pre(event.text_diff)}"
170213
)
171214
return self._append_permalink_and_trigger(body, event, "reply_delete")
@@ -176,7 +219,7 @@ def format_document_reply_deleted(self, event: LogEntryReplyDeleteEvent) -> str:
176219
f"Reply deleted from document {event.parent_log_document_info.name}<br/>"
177220
f"Deleted by: {event.event_triggered_by_username}<br/>"
178221
f"On entry by: {event.parent_log_info.entered_by_username}<br/>"
179-
f"Time: {event.event_timestamp}<br/>"
222+
f"Time: {self._format_timestamp(event.event_timestamp)}<br/>"
180223
f"<br/>Deleted reply content: {self._format_text_diff_pre(event.text_diff)}"
181224
)
182225
return self._append_permalink_and_trigger(body, event, "document_owner")

tools/developer_tools/bely-mqtt-message-broker/examples/handlers/apprise_smart_notification/handler.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,12 @@ def __init__(
6767

6868
# Initialize components
6969
self.config_loader = ConfigLoader(self.logger)
70-
self.formatter = NotificationFormatter(self.bely_url, self.logger)
70+
71+
# Initialize formatter with timezone from config if available
72+
self.timezone = None
73+
self.formatter = NotificationFormatter(
74+
self.bely_url, self.logger
75+
) # Will be updated after config load
7176
self.processor = NotificationProcessor(self.logger)
7277

7378
# Load configuration

0 commit comments

Comments
 (0)