##// END OF EJS Templates
code garden
marcink -
r2077:17960433 beta
parent child Browse files
Show More
@@ -1,143 +1,148 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.comment
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 comments model for RhodeCode
7 7
8 8 :created_on: Nov 11, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons.i18n.translation import _
30 30 from sqlalchemy.util.compat import defaultdict
31 31
32 32 from rhodecode.lib import extract_mentioned_users
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.model import BaseModel
35 35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
36 36 from rhodecode.model.notification import NotificationModel
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class ChangesetCommentsModel(BaseModel):
42 42
43 43 def __get_changeset_comment(self, changeset_comment):
44 44 return self._get_instance(ChangesetComment, changeset_comment)
45 45
46 46 def _extract_mentions(self, s):
47 47 user_objects = []
48 48 for username in extract_mentioned_users(s):
49 49 user_obj = User.get_by_username(username, case_insensitive=True)
50 50 if user_obj:
51 51 user_objects.append(user_obj)
52 52 return user_objects
53 53
54 54 def create(self, text, repo_id, user_id, revision, f_path=None,
55 55 line_no=None):
56 56 """
57 57 Creates new comment for changeset
58 58
59 59 :param text:
60 60 :param repo_id:
61 61 :param user_id:
62 62 :param revision:
63 63 :param f_path:
64 64 :param line_no:
65 65 """
66 66 if text:
67 67 repo = Repository.get(repo_id)
68 68 cs = repo.scm_instance.get_changeset(revision)
69 69 desc = cs.message
70 70 author = cs.author_email
71 71 comment = ChangesetComment()
72 72 comment.repo = repo
73 73 comment.user_id = user_id
74 74 comment.revision = revision
75 75 comment.text = text
76 76 comment.f_path = f_path
77 77 comment.line_no = line_no
78 78
79 79 self.sa.add(comment)
80 80 self.sa.flush()
81 81
82 82 # make notification
83 83 line = ''
84 84 if line_no:
85 85 line = _('on line %s') % line_no
86 86 subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \
87 87 {'commit_desc': desc, 'line': line},
88 88 h.url('changeset_home', repo_name=repo.repo_name,
89 89 revision=revision,
90 90 anchor='comment-%s' % comment.comment_id,
91 91 qualified=True,
92 92 )
93 93 )
94 94 body = text
95
96 # get the current participants of this changeset
95 97 recipients = ChangesetComment.get_users(revision=revision)
98
96 99 # add changeset author
97 100 recipients += [User.get_by_email(author)]
98 101
99 NotificationModel().create(created_by=user_id, subject=subj,
100 body=body, recipients=recipients,
101 type_=Notification.TYPE_CHANGESET_COMMENT)
102 NotificationModel().create(
103 created_by=user_id, subject=subj, body=body,
104 recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT
105 )
102 106
103 107 mention_recipients = set(self._extract_mentions(body))\
104 108 .difference(recipients)
105 109 if mention_recipients:
106 110 subj = _('[Mention]') + ' ' + subj
107 NotificationModel().create(created_by=user_id, subject=subj,
108 body=body,
109 recipients=mention_recipients,
110 type_=Notification.TYPE_CHANGESET_COMMENT)
111 NotificationModel().create(
112 created_by=user_id, subject=subj, body=body,
113 recipients=mention_recipients,
114 type_=Notification.TYPE_CHANGESET_COMMENT
115 )
111 116
112 117 return comment
113 118
114 119 def delete(self, comment):
115 120 """
116 121 Deletes given comment
117 122
118 123 :param comment_id:
119 124 """
120 125 comment = self.__get_changeset_comment(comment)
121 126 self.sa.delete(comment)
122 127
123 128 return comment
124 129
125 130 def get_comments(self, repo_id, revision):
126 131 return ChangesetComment.query()\
127 132 .filter(ChangesetComment.repo_id == repo_id)\
128 133 .filter(ChangesetComment.revision == revision)\
129 134 .filter(ChangesetComment.line_no == None)\
130 135 .filter(ChangesetComment.f_path == None).all()
131 136
132 137 def get_inline_comments(self, repo_id, revision):
133 138 comments = self.sa.query(ChangesetComment)\
134 139 .filter(ChangesetComment.repo_id == repo_id)\
135 140 .filter(ChangesetComment.revision == revision)\
136 141 .filter(ChangesetComment.line_no != None)\
137 142 .filter(ChangesetComment.f_path != None).all()
138 143
139 144 paths = defaultdict(lambda: defaultdict(list))
140 145
141 146 for co in comments:
142 147 paths[co.f_path][co.line_no].append(co)
143 148 return paths.items()
@@ -1,216 +1,222 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.notification
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Model for notifications
7 7
8 8
9 9 :created_on: Nov 20, 2011
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import logging
29 29 import traceback
30 30 import datetime
31 31
32 32 from pylons.i18n.translation import _
33 33
34 34 import rhodecode
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.model import BaseModel
37 37 from rhodecode.model.db import Notification, User, UserNotification
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 class NotificationModel(BaseModel):
43 43
44 44 def __get_user(self, user):
45 45 return self._get_instance(User, user, callback=User.get_by_username)
46 46
47 47 def __get_notification(self, notification):
48 48 if isinstance(notification, Notification):
49 49 return notification
50 50 elif isinstance(notification, int):
51 51 return Notification.get(notification)
52 52 else:
53 53 if notification:
54 54 raise Exception('notification must be int or Instance'
55 55 ' of Notification got %s' % type(notification))
56 56
57 57 def create(self, created_by, subject, body, recipients=None,
58 58 type_=Notification.TYPE_MESSAGE, with_email=True,
59 59 email_kwargs={}):
60 60 """
61 61
62 62 Creates notification of given type
63 63
64 64 :param created_by: int, str or User instance. User who created this
65 65 notification
66 66 :param subject:
67 67 :param body:
68 68 :param recipients: list of int, str or User objects, when None
69 69 is given send to all admins
70 70 :param type_: type of notification
71 71 :param with_email: send email with this notification
72 72 :param email_kwargs: additional dict to pass as args to email template
73 73 """
74 74 from rhodecode.lib.celerylib import tasks, run_task
75 75
76 76 if recipients and not getattr(recipients, '__iter__', False):
77 77 raise Exception('recipients must be a list of iterable')
78 78
79 79 created_by_obj = self.__get_user(created_by)
80 80
81 81 if recipients:
82 82 recipients_objs = []
83 83 for u in recipients:
84 84 obj = self.__get_user(u)
85 85 if obj:
86 86 recipients_objs.append(obj)
87 87 recipients_objs = set(recipients_objs)
88 log.debug('sending notifications %s to %s' % (
89 type_, recipients_objs)
90 )
88 91 else:
89 92 # empty recipients means to all admins
90 93 recipients_objs = User.query().filter(User.admin == True).all()
91
92 notif = Notification.create(created_by=created_by_obj, subject=subject,
93 body=body, recipients=recipients_objs,
94 type_=type_)
94 log.debug('sending notifications %s to admins: %s' % (
95 type_, recipients_objs)
96 )
97 notif = Notification.create(
98 created_by=created_by_obj, subject=subject,
99 body=body, recipients=recipients_objs, type_=type_
100 )
95 101
96 102 if with_email is False:
97 103 return notif
98 104
99 105 # send email with notification
100 106 for rec in recipients_objs:
101 107 email_subject = NotificationModel().make_description(notif, False)
102 108 type_ = type_
103 109 email_body = body
104 110 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
105 111 kwargs.update(email_kwargs)
106 112 email_body_html = EmailNotificationModel()\
107 113 .get_email_tmpl(type_, **kwargs)
108 114 run_task(tasks.send_email, rec.email, email_subject, email_body,
109 115 email_body_html)
110 116
111 117 return notif
112 118
113 119 def delete(self, user, notification):
114 120 # we don't want to remove actual notification just the assignment
115 121 try:
116 122 notification = self.__get_notification(notification)
117 123 user = self.__get_user(user)
118 124 if notification and user:
119 125 obj = UserNotification.query()\
120 126 .filter(UserNotification.user == user)\
121 127 .filter(UserNotification.notification
122 128 == notification)\
123 129 .one()
124 130 self.sa.delete(obj)
125 131 return True
126 132 except Exception:
127 133 log.error(traceback.format_exc())
128 134 raise
129 135
130 136 def get_for_user(self, user):
131 137 user = self.__get_user(user)
132 138 return user.notifications
133 139
134 140 def mark_all_read_for_user(self, user):
135 141 user = self.__get_user(user)
136 142 UserNotification.query()\
137 143 .filter(UserNotification.read==False)\
138 144 .update({'read': True})
139 145
140 146 def get_unread_cnt_for_user(self, user):
141 147 user = self.__get_user(user)
142 148 return UserNotification.query()\
143 149 .filter(UserNotification.read == False)\
144 150 .filter(UserNotification.user == user).count()
145 151
146 152 def get_unread_for_user(self, user):
147 153 user = self.__get_user(user)
148 154 return [x.notification for x in UserNotification.query()\
149 155 .filter(UserNotification.read == False)\
150 156 .filter(UserNotification.user == user).all()]
151 157
152 158 def get_user_notification(self, user, notification):
153 159 user = self.__get_user(user)
154 160 notification = self.__get_notification(notification)
155 161
156 162 return UserNotification.query()\
157 163 .filter(UserNotification.notification == notification)\
158 164 .filter(UserNotification.user == user).scalar()
159 165
160 166 def make_description(self, notification, show_age=True):
161 167 """
162 168 Creates a human readable description based on properties
163 169 of notification object
164 170 """
165 171
166 172 _map = {notification.TYPE_CHANGESET_COMMENT:_('commented on commit'),
167 173 notification.TYPE_MESSAGE:_('sent message'),
168 174 notification.TYPE_MENTION:_('mentioned you'),
169 175 notification.TYPE_REGISTRATION:_('registered in RhodeCode')}
170 176
171 177 DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
172 178
173 179 tmpl = "%(user)s %(action)s %(when)s"
174 180 if show_age:
175 181 when = h.age(notification.created_on)
176 182 else:
177 183 DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
178 184 when = DTF(notification.created_on)
179 185 data = dict(user=notification.created_by_user.username,
180 186 action=_map[notification.type_],
181 187 when=when)
182 188 return tmpl % data
183 189
184 190
185 191 class EmailNotificationModel(BaseModel):
186 192
187 193 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
188 194 TYPE_PASSWORD_RESET = 'passoword_link'
189 195 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
190 196 TYPE_DEFAULT = 'default'
191 197
192 198 def __init__(self):
193 199 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
194 200 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
195 201
196 202 self.email_types = {
197 203 self.TYPE_CHANGESET_COMMENT:'email_templates/changeset_comment.html',
198 204 self.TYPE_PASSWORD_RESET:'email_templates/password_reset.html',
199 205 self.TYPE_REGISTRATION:'email_templates/registration.html',
200 206 self.TYPE_DEFAULT:'email_templates/default.html'
201 207 }
202 208
203 209 def get_email_tmpl(self, type_, **kwargs):
204 210 """
205 211 return generated template for email based on given type
206 212
207 213 :param type_:
208 214 """
209 215
210 216 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
211 217 email_template = self._tmpl_lookup.get_template(base)
212 218 # translator inject
213 219 _kwargs = {'_':_}
214 220 _kwargs.update(kwargs)
215 221 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
216 222 return email_template.render(**_kwargs)
General Comments 0
You need to be logged in to leave comments. Login now