SIENTIAPDE-1172
feat: enhance email and slot manager activities with new filtering and caching functionalities - Updated Email class to improve attachment handling and logging. - Refactored Formatters class to process notifications more effectively by grouping them based on their configurations. - Introduced new methods in SlotManager for filtering notification alerts and storing notification cache. - Enhanced Alerts workflow to integrate new filtering and caching activities. - Added tests for new functionalities in SlotManager and Formatters classes to ensure reliability.
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
from email import encoders
|
||||
from email.mime.base import MIMEBase
|
||||
import traceback
|
||||
|
||||
from temporalio import workflow, activity
|
||||
|
||||
with workflow.unsafe.imports_passed_through():
|
||||
import traceback
|
||||
import smtplib
|
||||
from typing import Any
|
||||
from sientia_do.temporal.activities.base import BaseActivity
|
||||
@@ -12,6 +11,8 @@ with workflow.unsafe.imports_passed_through():
|
||||
from orchestrator.utils.email_builder import EmailBuilder
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.base import MIMEBase
|
||||
from email import encoders
|
||||
|
||||
|
||||
class Email(BaseActivity):
|
||||
@@ -24,7 +25,6 @@ class Email(BaseActivity):
|
||||
self.sender_email = sender_email
|
||||
self.sender_password = sender_password
|
||||
self.port = port
|
||||
self.logger = logger
|
||||
|
||||
if self.sender_password:
|
||||
self.server = smtplib.SMTP_SSL(smpt_server, port)
|
||||
@@ -68,7 +68,7 @@ class Email(BaseActivity):
|
||||
Attaches a list of attachments to an email message.
|
||||
Args:
|
||||
attachments (List[Dict]): A list of dictionaries where each dictionary contains
|
||||
the keys 'attachment_id', 'trigger', and 'timestamp' representing the attachment details.
|
||||
the keys 'filename' and 'content' representing the attachment details.
|
||||
msg (MIMEMultipart): The email message object to which the attachments will be added.
|
||||
Returns:
|
||||
MIMEMultipart: The email message object with the attachments added.
|
||||
@@ -124,7 +124,11 @@ class Email(BaseActivity):
|
||||
msg['Subject'] = f"SIENTIA™ {mail_type}"
|
||||
|
||||
msg = self.handle_attachments(
|
||||
[notification['attachment_content']
|
||||
[
|
||||
{
|
||||
"filename": f"{notification['trigger']}_{notification['notification_id']}.txt",
|
||||
"content": notification['attachment_content']
|
||||
}
|
||||
for notification in group_config['notifications']
|
||||
if notification['attachment_content']],
|
||||
msg)
|
||||
|
||||
@@ -508,28 +508,30 @@ class Formatters(BaseActivity):
|
||||
|
||||
for group_name, group_config in receiver_groups.items():
|
||||
|
||||
notification_id = group_config['notification_id']
|
||||
trigger = group_config['trigger']
|
||||
for notification in group_config['notifications']:
|
||||
|
||||
key = f"{notification_id}:{trigger}"
|
||||
notification_id = notification['notification_id']
|
||||
trigger = notification['trigger']
|
||||
|
||||
if key not in data:
|
||||
data[key] = {
|
||||
'status': group_config['status'],
|
||||
'timestamp': group_config['timestamp'],
|
||||
'groups': [group_name],
|
||||
'message': group_config['message'],
|
||||
'level': group_config['level'],
|
||||
'notification_id': notification_id,
|
||||
'block': group_config['block'],
|
||||
'schedule': trigger,
|
||||
'pipeline': group_config['pipeline'],
|
||||
'project': group_config['project'],
|
||||
'model_name': group_config['model_name'],
|
||||
'model_id': group_config['model_id'],
|
||||
'mail_type': mail_type
|
||||
}
|
||||
else:
|
||||
data[key]['groups'].append(group_name)
|
||||
key = f"{notification_id}:{trigger}"
|
||||
|
||||
if key not in data:
|
||||
data[key] = {
|
||||
'status': group_config['status'],
|
||||
'timestamp': notification['timestamp'],
|
||||
'groups': [group_name],
|
||||
'message': notification['message'],
|
||||
'level': notification['level'],
|
||||
'notification_id': notification_id,
|
||||
'block': notification['block'],
|
||||
'schedule': trigger,
|
||||
'pipeline': notification['pipeline'],
|
||||
'project': notification['project'],
|
||||
'model_name': notification['model_name'],
|
||||
'model_id': notification['model_id'],
|
||||
'mail_type': mail_type
|
||||
}
|
||||
else:
|
||||
data[key]['groups'].append(group_name)
|
||||
|
||||
return DataFrame(list(data.values())).to_dict()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from pandas import DataFrame
|
||||
|
||||
from temporalio import activity, workflow
|
||||
|
||||
with workflow.unsafe.imports_passed_through():
|
||||
@@ -9,6 +9,9 @@ with workflow.unsafe.imports_passed_through():
|
||||
from sientia_do.temporal.activities.redis_base import Redis
|
||||
from sientia_do.notifications.handlers import CoreNotificationHandler as NotificationHandler
|
||||
from sientia_do.notifications.models import NotificationLevel
|
||||
from orchestrator.utils.patterns import DEFAULT_DATE_FORMAT
|
||||
from pandas import DataFrame
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class SlotManager(Redis):
|
||||
@@ -263,3 +266,74 @@ class SlotManager(Redis):
|
||||
raise e
|
||||
|
||||
return last_data_timestamp
|
||||
|
||||
@activity.defn(name="filter_notification_alerts")
|
||||
async def filter_notification_alerts(self, input_data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Filter notification alerts
|
||||
"""
|
||||
metadata = input_data['metadata']
|
||||
notification_package = input_data['notification_package']
|
||||
sending_configs = input_data['sending_configs']
|
||||
notification_ttl = input_data['notification_ttl']
|
||||
|
||||
self.info("Filtering notification alerts...", metadata=metadata)
|
||||
|
||||
receiver_groups = {}
|
||||
|
||||
for receiver_group in sending_configs:
|
||||
group_name = receiver_group['group_name']
|
||||
receiver_groups[group_name] = {
|
||||
**receiver_group,
|
||||
"notifications": []
|
||||
}
|
||||
receiver_groups[group_name]['notifications'] = []
|
||||
|
||||
ignore_list = receiver_group.get('ignore', [])
|
||||
|
||||
for notification in notification_package:
|
||||
alert_type = "do_nothing"
|
||||
notification_id = notification['notification_id']
|
||||
# Check if notification was recently sent
|
||||
key = f"{notification['trigger']}:{notification_id}"
|
||||
|
||||
last_sent = self.get(key)
|
||||
|
||||
if last_sent is None:
|
||||
alert_type = "core_alerts"
|
||||
|
||||
else:
|
||||
last_sent = datetime.strptime(
|
||||
last_sent, DEFAULT_DATE_FORMAT)
|
||||
|
||||
# Check if "notification_ttl" seconds has passed since last sent
|
||||
if (datetime.now() - last_sent) > timedelta(seconds=notification_ttl):
|
||||
alert_type = "persistent_alerts"
|
||||
|
||||
# Check if this group must be notified
|
||||
if alert_type in receiver_group['contents'] and notification_id not in ignore_list:
|
||||
receiver_groups[group_name]["notifications"].append(
|
||||
notification)
|
||||
|
||||
return receiver_groups
|
||||
|
||||
@activity.defn(name="store_notification_cache")
|
||||
async def store_notification_cache(self, input_data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Store notification cache
|
||||
"""
|
||||
metadata = input_data['metadata']
|
||||
log_report = DataFrame(input_data['log_report'])
|
||||
sent_ttl = input_data['sent_ttl']
|
||||
|
||||
self.info("Storing notification cache...", metadata=metadata)
|
||||
|
||||
now = datetime.now().strftime(DEFAULT_DATE_FORMAT)
|
||||
|
||||
for index, row in log_report.iterrows():
|
||||
status = row['status']
|
||||
if status == 'sent':
|
||||
key = f"{row['schedule']}:{row['notification_id']}"
|
||||
self.set(key, now, ttl=sent_ttl)
|
||||
|
||||
return log_report
|
||||
|
||||
Reference in New Issue
Block a user