From cdbc9cea16d4b955ce6223d28d3f505a67e2a4ee Mon Sep 17 00:00:00 2001 From: Ramon Figueiredo Date: Tue, 14 Oct 2025 18:22:23 -0700 Subject: [PATCH] [cuegui] Add email notifications for subscription size/burst changes Added email notifications to track subscription modifications in the CueCommander GUI. When a subscription's size or burst value is changed, an automated email is sent to configured recipients with details about the change, including: - What was changed (size or burst) - Old and new values - Who made the change - Timestamp of the change Key features: - Configurable CC recipients via cuegui.yaml - Support for email templates with {show} and {domain} placeholders - Disabled by default (set email.subscription_change_cc to enable) - Graceful error handling - email failures don't block subscription changes Configuration: - New setting: email.subscription_change_cc in cuegui.yaml - Example: "{show}-admin@{domain},resource-team@{domain}" Files modified: - cuegui/config/cuegui.yaml: Added email.subscription_change_cc config - cuegui/Constants.py: Added SUBSCRIPTION_CHANGE_CC_TEMPLATE constant - cuegui/MenuActions.py: Implemented _sendSubscriptionChangeEmail() and updated editSize() and editBurst() methods --- cuegui/cuegui/Constants.py | 4 ++ cuegui/cuegui/MenuActions.py | 100 +++++++++++++++++++++++++++++++ cuegui/cuegui/config/cuegui.yaml | 8 +++ 3 files changed, 112 insertions(+) diff --git a/cuegui/cuegui/Constants.py b/cuegui/cuegui/Constants.py index 4cf7e99b7..59349a4be 100644 --- a/cuegui/cuegui/Constants.py +++ b/cuegui/cuegui/Constants.py @@ -158,6 +158,10 @@ def __get_version_from_cmd(command): SHOW_SUPPORT_CC_TEMPLATE = [val.strip() for val in __config.get('email.show_support_cc_template', '').split(',')] +SUBSCRIPTION_CHANGE_CC_TEMPLATE = [val.strip() + for val + in __config.get('email.subscription_change_cc', '').split(',') + if val.strip()] GITHUB_CREATE_ISSUE_URL = __config.get('links.issue.create') URL_USERGUIDE = __config.get('links.user_guide') diff --git a/cuegui/cuegui/MenuActions.py b/cuegui/cuegui/MenuActions.py index fcdae5d41..e511a8315 100644 --- a/cuegui/cuegui/MenuActions.py +++ b/cuegui/cuegui/MenuActions.py @@ -25,10 +25,18 @@ from builtins import filter from builtins import str from builtins import object +import datetime import getpass import glob +import smtplib import subprocess import time +try: + from email.MIMEText import MIMEText + from email.Header import Header +except ImportError: + from email.mime.text import MIMEText + from email.header import Header from qtpy import QtGui from qtpy import QtWidgets @@ -1663,6 +1671,82 @@ class SubscriptionActions(AbstractActions): def __init__(self, *args): AbstractActions.__init__(self, *args) + def _sendSubscriptionChangeEmail(self, subscription, change_type, old_value, new_value): + """Sends an email notification when a subscription is changed. + + @param subscription: The subscription object that was changed + @param change_type: Type of change ('size' or 'burst') + @param old_value: The previous value (in cores) + @param new_value: The new value (in cores) + """ + try: + username = getpass.getuser() + timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + # Extract show name from subscription + show_name = subscription.data.show_name + subscription_name = subscription.data.name + allocation_name = subscription.data.allocation_name + + # Construct email + subject = "Subscription {} Changed: {}".format(change_type.title(), subscription_name) + + body = """A subscription {} has been modified. + +Subscription: {} +Show: {} +Allocation: {} + +Change Details: + {}: {} -> {} + +Modified by: {} +Timestamp: {} + +This is an automated notification from CueCommander. +""".format(change_type, subscription_name, show_name, allocation_name, + change_type.title(), old_value, new_value, username, timestamp) + + # Email addresses + email_domain = cuegui.Constants.EMAIL_DOMAIN + if not email_domain: + logger.warning("EMAIL_DOMAIN not configured, skipping email notification") + return + + from_addr = "cuecommander@{}".format(email_domain) + + # Build CC list from configuration template + cc_list = [] + for template in cuegui.Constants.SUBSCRIPTION_CHANGE_CC_TEMPLATE: + if template: + cc_addr = template.format(show=show_name, domain=email_domain) + cc_list.append(cc_addr) + + # Skip sending email if no CC recipients are configured + if not cc_list: + logger.info( + "No CC recipients configured for subscription change notifications, " + "skipping email") + return + + # Create email message + msg = MIMEText(body, 'plain', 'utf-8') + msg["Subject"] = Header(subject, 'utf-8', continuation_ws=' ') + msg["From"] = from_addr + msg["To"] = from_addr + msg["Cc"] = ", ".join(cc_list) + + # Send email + recipient_list = [from_addr] + cc_list + server = smtplib.SMTP('smtp') + server.sendmail(from_addr, recipient_list, msg.as_string()) + server.quit() + + logger.info("Sent subscription change notification email for %s", subscription_name) + except Exception as e: + # Log error but don't fail the operation if email fails + logger.warning("Failed to send subscription change notification email: %s", e) + editSize_info = ["Edit Subscription Size...", None, "configure"] def editSize(self, rpcObjects=None): subs = self._getSelected(rpcObjects) @@ -1688,9 +1772,17 @@ def editSize(self, rpcObjects=None): return for sub in subs: + # Capture old value before change + old_size = sub.data.size / 100.0 + new_size = value + self.cuebotCall(sub.setSize, "Set Size on Subscription %s Failed" % sub.data.name, int(value*100.0)) + + # Send email notification + self._sendSubscriptionChangeEmail(sub, "size", old_size, new_size) + self._update() editBurst_info = ["Edit Subscription Burst...", None, "configure"] @@ -1708,9 +1800,17 @@ def editBurst(self, rpcObjects=None): decimalPlaces) if choice: for sub in subs: + # Capture old value before change + old_burst = sub.data.burst / 100.0 + new_burst = value + self.cuebotCall(sub.setBurst, "Set Burst on Subscription %s Failed" % sub.data.name, int(value*100.0)) + + # Send email notification + self._sendSubscriptionChangeEmail(sub, "burst", old_burst, new_burst) + self._update() delete_info = ["Delete Subscription", None, "configure"] diff --git a/cuegui/cuegui/config/cuegui.yaml b/cuegui/cuegui/config/cuegui.yaml index 77971d4e0..fe220123f 100644 --- a/cuegui/cuegui/config/cuegui.yaml +++ b/cuegui/cuegui/config/cuegui.yaml @@ -133,6 +133,14 @@ email.domain: 'your.domain.com' # - {show} is not required and will be replaced by the job show # - Multiple addresses might be provided in a comma-separated list email.show_support_cc_template: "{show}-support@{domain}" +# CC list for subscription change notifications (size/burst changes) +# - {domain} will be replaced by email.domain +# - {show} will be replaced by the subscription show name +# - Multiple addresses might be provided in a comma-separated list +# - Set to empty string ("") to disable email notifications for subscription changes +# - Example: "{show}-admin@{domain},resource-team@{domain}" +# - Example: "admin@example.com,team-lead@example.com" +email.subscription_change_cc: "" # Unix epoch timestamp. If the user last viewed the startup notice before this time, the # notice will be shown.