Browse Source

Merge pull request #334 from tracim/fix/278/log_notification_metadata

Damien Accorsi 7 years ago
parent
commit
a2e4b27d49

+ 1 - 0
tracim/development.ini.base View File

184
 website.server_name = 127.0.0.1
184
 website.server_name = 127.0.0.1
185
 
185
 
186
 email.notification.activated = False
186
 email.notification.activated = False
187
+# email.notification.log_file_path = /tmp/mail-notifications.log
187
 # email notifications can be sent with the user_id added as an identifier
188
 # email notifications can be sent with the user_id added as an identifier
188
 # this way email clients like Thunderbird will be able to distinguish
189
 # this way email clients like Thunderbird will be able to distinguish
189
 # notifications generated by a user or another one
190
 # notifications generated by a user or another one

+ 1 - 0
tracim/test.ini View File

15
 radicale.client.base_url.host = http://localhost:15232
15
 radicale.client.base_url.host = http://localhost:15232
16
 radicale.client.base_url.prefix = /
16
 radicale.client.base_url.prefix = /
17
 email.notification.activated = false
17
 email.notification.activated = false
18
+email.notification.log_file_path = /tmp/mail-notifications-test.log
18
 
19
 
19
 [server:main]
20
 [server:main]
20
 use = egg:gearbox#wsgiref
21
 use = egg:gearbox#wsgiref

+ 4 - 0
tracim/tracim/config/app_cfg.py View File

339
         self.EMAIL_NOTIFICATION_SMTP_PASSWORD = tg.config.get(
339
         self.EMAIL_NOTIFICATION_SMTP_PASSWORD = tg.config.get(
340
             'email.notification.smtp.password',
340
             'email.notification.smtp.password',
341
         )
341
         )
342
+        self.EMAIL_NOTIFICATION_LOG_FILE_PATH = tg.config.get(
343
+            'email.notification.log_file_path',
344
+            None,
345
+        )
342
 
346
 
343
         self.TRACKER_JS_PATH = tg.config.get(
347
         self.TRACKER_JS_PATH = tg.config.get(
344
             'js_tracker_path',
348
             'js_tracker_path',

+ 9 - 6
tracim/tracim/lib/email.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 import smtplib
2
 import smtplib
3
+import typing
3
 from email.message import Message
4
 from email.message import Message
4
 from email.mime.multipart import MIMEMultipart
5
 from email.mime.multipart import MIMEMultipart
5
 from email.mime.text import MIMEText
6
 from email.mime.text import MIMEText
6
 
7
 
7
-import typing
8
 from mako.template import Template
8
 from mako.template import Template
9
 from tg.i18n import ugettext as _
9
 from tg.i18n import ugettext as _
10
 
10
 
11
 from tracim.lib.base import logger
11
 from tracim.lib.base import logger
12
-from tracim.model import User
13
-
14
 from tracim.lib.utils import get_rq_queue
12
 from tracim.lib.utils import get_rq_queue
13
+from tracim.model import User
15
 
14
 
16
 
15
 
17
 def send_email_through(
16
 def send_email_through(
53
         self.password = password
52
         self.password = password
54
 
53
 
55
 
54
 
56
-
57
 class EmailSender(object):
55
 class EmailSender(object):
58
     """
56
     """
59
     this class allow to send emails and has no relations with SQLAlchemy and other tg HTTP request environment
57
     this class allow to send emails and has no relations with SQLAlchemy and other tg HTTP request environment
86
             self._smtp_connection.quit()
84
             self._smtp_connection.quit()
87
             logger.info(self, 'Connection closed.')
85
             logger.info(self, 'Connection closed.')
88
 
86
 
89
-
90
     def send_mail(self, message: MIMEMultipart):
87
     def send_mail(self, message: MIMEMultipart):
91
         if not self._is_active:
88
         if not self._is_active:
92
             logger.info(self, 'Not sending email to {} (service desactivated)'.format(message['To']))
89
             logger.info(self, 'Not sending email to {} (service desactivated)'.format(message['To']))
93
         else:
90
         else:
94
-            self.connect() # Acutally, this connects to SMTP only if required
91
+            self.connect()  # Acutally, this connects to SMTP only if required
95
             logger.info(self, 'Sending email to {}'.format(message['To']))
92
             logger.info(self, 'Sending email to {}'.format(message['To']))
96
             self._smtp_connection.send_message(message)
93
             self._smtp_connection.send_message(message)
94
+            from tracim.lib.notifications import EmailNotifier
95
+            EmailNotifier.log_notification(
96
+                action='   SENT',
97
+                recipient=message['To'],
98
+                subject=message['Subject'],
99
+            )
97
 
100
 
98
 
101
 
99
 class EmailManager(object):
102
 class EmailManager(object):

+ 38 - 8
tracim/tracim/lib/notifications.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
+import datetime
3
+import typing
2
 
4
 
3
 from email.header import Header
5
 from email.header import Header
4
 from email.mime.multipart import MIMEMultipart
6
 from email.mime.multipart import MIMEMultipart
5
 from email.mime.text import MIMEText
7
 from email.mime.text import MIMEText
6
 
8
 
7
-import lxml
8
 from lxml.html.diff import htmldiff
9
 from lxml.html.diff import htmldiff
9
 
10
 
10
 from mako.template import Template
11
 from mako.template import Template
11
 
12
 
12
 from tracim.lib.base import logger
13
 from tracim.lib.base import logger
14
+from tracim.lib.email import EmailSender
13
 from tracim.lib.email import SmtpConfiguration
15
 from tracim.lib.email import SmtpConfiguration
14
 from tracim.lib.email import send_email_through
16
 from tracim.lib.email import send_email_through
15
-from tracim.lib.email import EmailSender
16
 from tracim.lib.user import UserApi
17
 from tracim.lib.user import UserApi
17
-from tracim.lib.workspace import WorkspaceApi
18
 from tracim.lib.utils import lazy_ugettext as l_
18
 from tracim.lib.utils import lazy_ugettext as l_
19
-from tracim.model.serializers import Context
19
+from tracim.lib.workspace import WorkspaceApi
20
+from tracim.model.auth import User
21
+from tracim.model.data import ActionDescription
22
+from tracim.model.data import Content
23
+from tracim.model.data import ContentType
24
+from tracim.model.data import UserRoleInWorkspace
20
 from tracim.model.serializers import CTX
25
 from tracim.model.serializers import CTX
26
+from tracim.model.serializers import Context
21
 from tracim.model.serializers import DictLikeClass
27
 from tracim.model.serializers import DictLikeClass
22
 
28
 
23
-from tracim.model.data import Content, UserRoleInWorkspace, ContentType, \
24
-    ActionDescription
25
-from tracim.model.auth import User
26
-
27
 
29
 
28
 class INotifier(object):
30
 class INotifier(object):
29
     """
31
     """
210
             email_address = email_address
212
             email_address = email_address
211
         )
213
         )
212
 
214
 
215
+    @staticmethod
216
+    def log_notification(
217
+            action: str,
218
+            recipient: typing.Optional[str],
219
+            subject: typing.Optional[str],
220
+    ) -> None:
221
+        """Log notification metadata."""
222
+        from tracim.config.app_cfg import CFG
223
+        log_path = CFG.get_instance().EMAIL_NOTIFICATION_LOG_FILE_PATH
224
+        if log_path:
225
+            # TODO - A.P - 2017-09-06 - file logging inefficiency
226
+            # Updating a document with 100 users to notify will leads to open
227
+            # and close the file 100 times.
228
+            with open(log_path, 'a') as log_file:
229
+                print(
230
+                    datetime.datetime.now(),
231
+                    action,
232
+                    recipient,
233
+                    subject,
234
+                    sep='|',
235
+                    file=log_file,
236
+                )
237
+
213
     def notify_content_update(self, event_actor_id: int, event_content_id: int):
238
     def notify_content_update(self, event_actor_id: int, event_content_id: int):
214
         """
239
         """
215
         Look for all users to be notified about the new content and send them an individual email
240
         Look for all users to be notified about the new content and send them an individual email
277
             message.attach(part1)
302
             message.attach(part1)
278
             message.attach(part2)
303
             message.attach(part2)
279
 
304
 
305
+            self.log_notification(
306
+                action='CREATED',
307
+                recipient=message['To'],
308
+                subject=message['Subject'],
309
+            )
280
             send_email_through(async_email_sender.send_mail, message)
310
             send_email_through(async_email_sender.send_mail, message)
281
 
311
 
282
     def _build_email_body(self, mako_template_filepath: str, role: UserRoleInWorkspace, content: Content, actor: User) -> str:
312
     def _build_email_body(self, mako_template_filepath: str, role: UserRoleInWorkspace, content: Content, actor: User) -> str:

+ 41 - 8
tracim/tracim/tests/library/test_notification.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
+import os
3
+import re
2
 
4
 
3
 from nose.tools import eq_
5
 from nose.tools import eq_
4
 from nose.tools import ok_
6
 from nose.tools import ok_
5
-from nose.tools import raises
6
-
7
-from sqlalchemy.orm.exc import NoResultFound
8
-
9
-import transaction
10
 
7
 
11
 from tracim.config.app_cfg import CFG
8
 from tracim.config.app_cfg import CFG
12
 from tracim.lib.notifications import DummyNotifier
9
 from tracim.lib.notifications import DummyNotifier
16
 from tracim.lib.notifications import RealNotifier
13
 from tracim.lib.notifications import RealNotifier
17
 from tracim.model.auth import User
14
 from tracim.model.auth import User
18
 from tracim.model.data import Content
15
 from tracim.model.data import Content
19
-
20
 from tracim.tests import TestStandard
16
 from tracim.tests import TestStandard
21
 
17
 
22
 
18
 
43
     def test_email_subject_tag_list(self):
39
     def test_email_subject_tag_list(self):
44
         tags = EST.all()
40
         tags = EST.all()
45
 
41
 
46
-        eq_(4,len(tags))
42
+        eq_(4, len(tags))
47
         ok_('{website_title}' in tags)
43
         ok_('{website_title}' in tags)
48
         ok_('{workspace_label}' in tags)
44
         ok_('{workspace_label}' in tags)
49
         ok_('{content_label}' in tags)
45
         ok_('{content_label}' in tags)
51
 
47
 
52
 
48
 
53
 class TestEmailNotifier(TestStandard):
49
 class TestEmailNotifier(TestStandard):
50
+
51
+    def test_unit__log_notification(self):
52
+        """Check file and format of notification log."""
53
+        log_path = CFG.get_instance().EMAIL_NOTIFICATION_LOG_FILE_PATH
54
+        pattern = '\|{act}\|{rec}\|{subj}$\\n'
55
+        line_1_act = 'CREATED'
56
+        line_1_rec = 'user 1 <us.er@o.ne>'
57
+        line_1_subj = 'notification 1'
58
+        line_1_pattern = pattern.format(
59
+            act=line_1_act,
60
+            rec=line_1_rec,
61
+            subj=line_1_subj,
62
+        )
63
+        line_2_act = '   SENT'
64
+        line_2_rec = 'user 2 <us.er@t.wo>'
65
+        line_2_subj = 'notification 2'
66
+        line_2_pattern = pattern.format(
67
+            act=line_2_act,
68
+            rec=line_2_rec,
69
+            subj=line_2_subj,
70
+        )
71
+        EmailNotifier.log_notification(
72
+            action=line_1_act,
73
+            recipient=line_1_rec,
74
+            subject=line_1_subj,
75
+        )
76
+        EmailNotifier.log_notification(
77
+            action=line_2_act,
78
+            recipient=line_2_rec,
79
+            subject=line_2_subj,
80
+        )
81
+        with open(log_path, 'rt') as log_file:
82
+            line_1 = log_file.readline()
83
+            line_2 = log_file.readline()
84
+        os.remove(path=log_path)
85
+        ok_(re.search(pattern=line_1_pattern, string=line_1))
86
+        ok_(re.search(pattern=line_2_pattern, string=line_2))
87
+
54
     def test_email_notifier__build_name_with_user_id(self):
88
     def test_email_notifier__build_name_with_user_id(self):
55
         u = User()
89
         u = User()
56
         u.user_id = 3
90
         u.user_id = 3
95
         notifier = EmailNotifier(smtp_config=None, global_config=config)
129
         notifier = EmailNotifier(smtp_config=None, global_config=config)
96
         email = notifier._get_sender()
130
         email = notifier._get_sender()
97
         eq_('Robot <noreply@tracim.io>', email)
131
         eq_('Robot <noreply@tracim.io>', email)
98
-