[1/2] notify.py: Encapsulate individual message recipient into a new class

Message ID 20181016031844.30857-2-archlinux@thecybershadow.net
State New
Headers show
Series Mention users' relationship to packages in comment notifications | expand

Commit Message

Vladimir Panteleev Oct. 16, 2018, 3:18 a.m. UTC
Allow Notification implementations to add additional fields to their
returned Recipient objects, so as to allow customizing the message
body/subject per recipient.
---
 aurweb/scripts/notify.py | 156 ++++++++++++++++++++++-----------------
 1 file changed, 87 insertions(+), 69 deletions(-)

Patch

diff --git a/aurweb/scripts/notify.py b/aurweb/scripts/notify.py
index 44eec84..c463823 100755
--- a/aurweb/scripts/notify.py
+++ b/aurweb/scripts/notify.py
@@ -41,6 +41,14 @@  def pkgbase_from_pkgreq(conn, reqid):
     return cur.fetchone()[0]
 
 
+class Recipient:
+    def __init__(self, to, lang):
+        self._to, self._lang = to, lang
+
+    def get_to(self):
+        return self._to
+
+
 class Notification:
     def __init__(self):
         self._l10n = aurweb.l10n.Translator()
@@ -51,9 +59,9 @@  class Notification:
     def get_headers(self):
         return {}
 
-    def get_body_fmt(self, lang):
+    def get_body_fmt(self, recipient):
         body = ''
-        for line in self.get_body(lang).splitlines():
+        for line in self.get_body(recipient).splitlines():
             body += textwrap.fill(line, break_long_words=False) + '\n'
         for i, ref in enumerate(self.get_refs()):
             body += '\n' + '[%d] %s' % (i + 1, ref)
@@ -65,10 +73,10 @@  class Notification:
         reply_to = aurweb.config.get('notifications', 'reply-to')
 
         for recipient in self.get_recipients():
-            to, lang = recipient
-            msg = email.mime.text.MIMEText(self.get_body_fmt(lang),
+            to = recipient.get_to()
+            msg = email.mime.text.MIMEText(self.get_body_fmt(recipient),
                                            'plain', 'utf-8')
-            msg['Subject'] = self.get_subject(lang)
+            msg['Subject'] = self.get_subject(recipient)
             msg['From'] = sender
             msg['Reply-to'] = reply_to
             msg['To'] = to
@@ -89,34 +97,34 @@  class ResetKeyNotification(Notification):
         super().__init__()
 
     def get_recipients(self):
-        return [(self._to, self._lang)]
+        return [Recipient(self._to, self._lang)]
 
-    def get_subject(self, lang):
-        return self._l10n.translate('AUR Password Reset', lang)
+    def get_subject(self, recipient):
+        return self._l10n.translate('AUR Password Reset', recipient._lang)
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         return self._l10n.translate(
                 'A password reset request was submitted for the account '
                 '{user} associated with your email address. If you wish to '
                 'reset your password follow the link [1] below, otherwise '
                 'ignore this message and nothing will happen.',
-                lang).format(user=self._username)
+                recipient._lang).format(user=self._username)
 
     def get_refs(self):
         return (aur_location + '/passreset/?resetkey=' + self._resetkey,)
 
 
 class WelcomeNotification(ResetKeyNotification):
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return self._l10n.translate('Welcome to the Arch User Repository',
-                                    lang)
+                                    recipient._lang)
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         return self._l10n.translate(
                 'Welcome to the Arch User Repository! In order to set an '
                 'initial password for your new account, please click the '
                 'link [1] below. If the link does not work, try copying and '
-                'pasting it into your browser.', lang)
+                'pasting it into your browser.', recipient._lang)
 
 
 class CommentNotification(Notification):
@@ -130,7 +138,7 @@  class CommentNotification(Notification):
                            'PackageNotifications.UserID != ? AND ' +
                            'PackageNotifications.PackageBaseID = ?',
                            [uid, pkgbase_id])
-        self._recipients = cur.fetchall()
+        self._recipients = [Recipient(to, lang) for to, lang in cur.fetchall()]
         cur = conn.execute('SELECT Comments FROM PackageComments WHERE ID = ?',
                            [comment_id])
         self._text = cur.fetchone()[0]
@@ -139,20 +147,22 @@  class CommentNotification(Notification):
     def get_recipients(self):
         return self._recipients
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return self._l10n.translate('AUR Comment for {pkgbase}',
-                                    lang).format(pkgbase=self._pkgbase)
+                                    recipient._lang).format(
+                                        pkgbase=self._pkgbase)
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         body = self._l10n.translate(
                 '{user} [1] added the following comment to {pkgbase} [2]:',
-                lang).format(user=self._user, pkgbase=self._pkgbase)
+                recipient._lang).format(user=self._user, pkgbase=self._pkgbase)
         body += '\n\n' + self._text + '\n\n'
-        dnlabel = self._l10n.translate('Disable notifications', lang)
+        dnlabel = self._l10n.translate('Disable notifications',
+                                       recipient._lang)
         body += self._l10n.translate(
                 'If you no longer wish to receive notifications about this '
                 'package, please go to the package page [2] and select '
-                '"{label}".', lang).format(label=dnlabel)
+                '"{label}".', recipient._lang).format(label=dnlabel)
         return body
 
     def get_refs(self):
@@ -177,27 +187,29 @@  class UpdateNotification(Notification):
                            'PackageNotifications.UserID != ? AND ' +
                            'PackageNotifications.PackageBaseID = ?',
                            [uid, pkgbase_id])
-        self._recipients = cur.fetchall()
+        self._recipients = [Recipient(to, lang) for to, lang in cur.fetchall()]
         super().__init__()
 
     def get_recipients(self):
         return self._recipients
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return self._l10n.translate('AUR Package Update: {pkgbase}',
-                                    lang).format(pkgbase=self._pkgbase)
+                                    recipient._lang).format(
+                                        pkgbase=self._pkgbase)
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         body = self._l10n.translate('{user} [1] pushed a new commit to '
-                                    '{pkgbase} [2].', lang).format(
+                                    '{pkgbase} [2].', recipient._lang).format(
                                             user=self._user,
                                             pkgbase=self._pkgbase)
         body += '\n\n'
-        dnlabel = self._l10n.translate('Disable notifications', lang)
+        dnlabel = self._l10n.translate('Disable notifications',
+                                       recipient._lang)
         body += self._l10n.translate(
                 'If you no longer wish to receive notifications about this '
                 'package, please go to the package page [2] and select '
-                '"{label}".', lang).format(label=dnlabel)
+                '"{label}".', recipient._lang).format(label=dnlabel)
         return body
 
     def get_refs(self):
@@ -222,7 +234,7 @@  class FlagNotification(Notification):
                            'ON PackageBases.MaintainerUID = Users.ID OR ' +
                            'PackageBases.ID = PackageComaintainers.PackageBaseID ' +
                            'WHERE PackageBases.ID = ?', [pkgbase_id])
-        self._recipients = cur.fetchall()
+        self._recipients = [Recipient(to, lang) for to, lang in cur.fetchall()]
         cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
                            'ID = ?', [pkgbase_id])
         self._text = cur.fetchone()[0]
@@ -231,16 +243,17 @@  class FlagNotification(Notification):
     def get_recipients(self):
         return self._recipients
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return self._l10n.translate('AUR Out-of-date Notification for '
                                     '{pkgbase}',
-                                    lang).format(pkgbase=self._pkgbase)
+                                    recipient._lang).format(
+                                        pkgbase=self._pkgbase)
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         body = self._l10n.translate(
                 'Your package {pkgbase} [1] has been flagged out-of-date by '
-                '{user} [2]:', lang).format(pkgbase=self._pkgbase,
-                                            user=self._user)
+                '{user} [2]:', recipient._lang).format(pkgbase=self._pkgbase,
+                                                       user=self._user)
         body += '\n\n' + self._text
         return body
 
@@ -261,7 +274,7 @@  class OwnershipEventNotification(Notification):
                            'PackageNotifications.UserID != ? AND ' +
                            'PackageNotifications.PackageBaseID = ?',
                            [uid, pkgbase_id])
-        self._recipients = cur.fetchall()
+        self._recipients = [Recipient(to, lang) for to, lang in cur.fetchall()]
         cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
                            'ID = ?', [pkgbase_id])
         self._text = cur.fetchone()[0]
@@ -270,9 +283,10 @@  class OwnershipEventNotification(Notification):
     def get_recipients(self):
         return self._recipients
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return self._l10n.translate('AUR Ownership Notification for {pkgbase}',
-                                    lang).format(pkgbase=self._pkgbase)
+                                    recipient._lang).format(
+                                        pkgbase=self._pkgbase)
 
     def get_refs(self):
         return (aur_location + '/pkgbase/' + self._pkgbase + '/',
@@ -280,18 +294,18 @@  class OwnershipEventNotification(Notification):
 
 
 class AdoptNotification(OwnershipEventNotification):
-    def get_body(self, lang):
+    def get_body(self, recipient):
         return self._l10n.translate(
                 'The package {pkgbase} [1] was adopted by {user} [2].',
-                lang).format(pkgbase=self._pkgbase, user=self._user)
+                recipient._lang).format(pkgbase=self._pkgbase, user=self._user)
 
 
 class DisownNotification(OwnershipEventNotification):
-    def get_body(self, lang):
+    def get_body(self, recipient):
         return self._l10n.translate(
                 'The package {pkgbase} [1] was disowned by {user} '
-                '[2].', lang).format(pkgbase=self._pkgbase,
-                                     user=self._user)
+                '[2].', recipient._lang).format(pkgbase=self._pkgbase,
+                                                user=self._user)
 
 
 class ComaintainershipEventNotification(Notification):
@@ -303,29 +317,30 @@  class ComaintainershipEventNotification(Notification):
         super().__init__()
 
     def get_recipients(self):
-        return [(self._to, self._lang)]
+        return [Recipient(self._to, self._lang)]
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return self._l10n.translate('AUR Co-Maintainer Notification for '
                                     '{pkgbase}',
-                                    lang).format(pkgbase=self._pkgbase)
+                                    recipient._lang).format(
+                                        pkgbase=self._pkgbase)
 
     def get_refs(self):
         return (aur_location + '/pkgbase/' + self._pkgbase + '/',)
 
 
 class ComaintainerAddNotification(ComaintainershipEventNotification):
-    def get_body(self, lang):
+    def get_body(self, recipient):
         return self._l10n.translate(
                 'You were added to the co-maintainer list of {pkgbase} [1].',
-                lang).format(pkgbase=self._pkgbase)
+                recipient._lang).format(pkgbase=self._pkgbase)
 
 
 class ComaintainerRemoveNotification(ComaintainershipEventNotification):
-    def get_body(self, lang):
+    def get_body(self, recipient):
         return self._l10n.translate(
                 'You were removed from the co-maintainer list of {pkgbase} '
-                '[1].', lang).format(pkgbase=self._pkgbase)
+                '[1].', recipient._lang).format(pkgbase=self._pkgbase)
 
 
 class DeleteNotification(Notification):
@@ -343,31 +358,34 @@  class DeleteNotification(Notification):
                            'PackageNotifications.UserID != ? AND ' +
                            'PackageNotifications.PackageBaseID = ?',
                            [uid, old_pkgbase_id])
-        self._recipients = cur.fetchall()
+        self._recipients = [Recipient(to, lang) for to, lang in cur.fetchall()]
         super().__init__()
 
     def get_recipients(self):
         return self._recipients
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return self._l10n.translate('AUR Package deleted: {pkgbase}',
-                                    lang).format(pkgbase=self._old_pkgbase)
+                                    recipient._lang).format(
+                                        pkgbase=self._old_pkgbase)
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         if self._new_pkgbase:
-            dnlabel = self._l10n.translate('Disable notifications', lang)
+            dnlabel = self._l10n.translate('Disable notifications',
+                                           recipient._lang)
             return self._l10n.translate(
                     '{user} [1] merged {old} [2] into {new} [3].\n\n'
                     'If you no longer wish receive notifications about the '
                     'new package, please go to [3] and click "{label}".',
-                    lang).format(user=self._user, old=self._old_pkgbase,
-                                 new=self._new_pkgbase, label=dnlabel)
+                    recipient._lang).format(
+                        user=self._user, old=self._old_pkgbase,
+                        new=self._new_pkgbase, label=dnlabel)
         else:
             return self._l10n.translate(
                     '{user} [1] deleted {pkgbase} [2].\n\n'
                     'You will no longer receive notifications about this '
-                    'package.', lang).format(user=self._user,
-                                             pkgbase=self._old_pkgbase)
+                    'package.', recipient._lang).format(
+                        user=self._user, pkgbase=self._old_pkgbase)
 
     def get_refs(self):
         refs = (aur_location + '/account/' + self._user + '/',
@@ -398,13 +416,13 @@  class RequestOpenNotification(Notification):
         self._merge_into = merge_into
 
     def get_recipients(self):
-        return [(self._to, 'en')]
+        return [Recipient(self._to, 'en')]
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return '[PRQ#%d] %s Request for %s' % \
                (self._reqid, self._reqtype.title(), self._pkgbase)
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         if self._merge_into:
             body = '%s [1] filed a request to merge %s [2] into %s [3]:' % \
                    (self._user, self._pkgbase, self._merge_into)
@@ -455,15 +473,15 @@  class RequestCloseNotification(Notification):
         self._reason = reason
 
     def get_recipients(self):
-        return [(self._to, 'en')]
+        return [Recipient(self._to, 'en')]
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return '[PRQ#%d] %s Request for %s %s' % (self._reqid,
                                                   self._reqtype.title(),
                                                   self._pkgbase,
                                                   self._reason.title())
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         if self._user:
             body = 'Request #%d has been %s by %s [1]' % \
                    (self._reqid, self._reason, self._user)
@@ -497,21 +515,21 @@  class TUVoteReminderNotification(Notification):
                            'WHERE AccountTypeID IN (2, 4) AND ID NOT IN ' +
                            '(SELECT UserID FROM TU_Votes ' +
                            'WHERE TU_Votes.VoteID = ?)', [vote_id])
-        self._recipients = cur.fetchall()
+        self._recipients = [Recipient(to, lang) for to, lang in cur.fetchall()]
         super().__init__()
 
     def get_recipients(self):
         return self._recipients
 
-    def get_subject(self, lang):
+    def get_subject(self, recipient):
         return self._l10n.translate('TU Vote Reminder: Proposal {id}',
-                                    lang).format(id=self._vote_id)
+                                    recipient._lang).format(id=self._vote_id)
 
-    def get_body(self, lang):
+    def get_body(self, recipient):
         return self._l10n.translate(
                 'Please remember to cast your vote on proposal {id} [1]. '
                 'The voting period ends in less than 48 hours.',
-                lang).format(id=self._vote_id)
+                recipient._lang).format(id=self._vote_id)
 
     def get_refs(self):
         return (aur_location + '/tu/?id=' + str(self._vote_id),)