pull-requests: added update pull-requests email+notifications...
marcink -
r4120:7cd93c2b default
Not Reviewed
Show More
Add another comment
TODOs: 0 unresolved 0 Resolved
COMMENTS: 0 General 0 Inline
@@ -0,0 +1,164
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
3 <%namespace name="base" file="base.mako"/>
4
5 ## EMAIL SUBJECT
6 <%def name="subject()" filter="n,trim,whitespace_filter">
7 <%
8 data = {
9 'updating_user': '@'+h.person(updating_user),
10 'pr_id': pull_request.pull_request_id,
11 'pr_title': pull_request.title,
12 }
13 %>
14
15 ${_('{updating_user} updated pull request. !{pr_id}: "{pr_title}"').format(**data) |n}
16 </%def>
17
18 ## PLAINTEXT VERSION OF BODY
19 <%def name="body_plaintext()" filter="n,trim">
20 <%
21 data = {
22 'updating_user': h.person(updating_user),
23 'pr_id': pull_request.pull_request_id,
24 'pr_title': pull_request.title,
25 'source_ref_type': pull_request.source_ref_parts.type,
26 'source_ref_name': pull_request.source_ref_parts.name,
27 'target_ref_type': pull_request.target_ref_parts.type,
28 'target_ref_name': pull_request.target_ref_parts.name,
29 'repo_url': pull_request_source_repo_url,
30 'source_repo': pull_request_source_repo.repo_name,
31 'target_repo': pull_request_target_repo.repo_name,
32 'source_repo_url': pull_request_source_repo_url,
33 'target_repo_url': pull_request_target_repo_url,
34 }
35 %>
36
37 * ${_('Pull Request link')}: ${pull_request_url}
38
39 * ${h.literal(_('Commit flow: {source_ref_type}:{source_ref_name} of {source_repo_url} into {target_ref_type}:{target_ref_name} of {target_repo_url}').format(**data))}
40
41 * ${_('Title')}: ${pull_request.title}
42
43 * ${_('Description')}:
44
45 ${pull_request.description | trim}
46
47 * Changed commits:
48
49 - Added: ${len(added_commits)}
50 - Removed: ${len(removed_commits)}
51
52 * Changed files:
53
54 %if not changed_files:
55 No file changes found
56 %else:
57 %for file_name in added_files:
58 - A `${file_name}`
59 %endfor
60 %for file_name in modified_files:
61 - M `${file_name}`
62 %endfor
63 %for file_name in removed_files:
64 - R `${file_name}`
65 %endfor
66 %endif
67
68 ---
69 ${self.plaintext_footer()}
70 </%def>
71 <%
72 data = {
73 'updating_user': h.person(updating_user),
74 'pr_id': pull_request.pull_request_id,
75 'pr_title': pull_request.title,
76 'source_ref_type': pull_request.source_ref_parts.type,
77 'source_ref_name': pull_request.source_ref_parts.name,
78 'target_ref_type': pull_request.target_ref_parts.type,
79 'target_ref_name': pull_request.target_ref_parts.name,
80 'repo_url': pull_request_source_repo_url,
81 'source_repo': pull_request_source_repo.repo_name,
82 'target_repo': pull_request_target_repo.repo_name,
83 'source_repo_url': h.link_to(pull_request_source_repo.repo_name, pull_request_source_repo_url),
84 'target_repo_url': h.link_to(pull_request_target_repo.repo_name, pull_request_target_repo_url),
85 }
86 %>
87
88 <table style="text-align:left;vertical-align:middle;width: 100%">
89 <tr>
90 <td style="width:100%;border-bottom:1px solid #dbd9da;">
91
92 <h4 style="margin: 0">
93 <div style="margin-bottom: 4px">
94 <span style="color:#7E7F7F">@${h.person(updating_user.username)}</span>
95 ${_('updated')}
96 <a href="${pull_request_url}" style="${base.link_css()}">
97 ${_('pull request.').format(**data) }
98 </a>
99 </div>
100 <div style="margin-top: 10px"></div>
101 ${_('Pull request')} <code>!${data['pr_id']}: ${data['pr_title']}</code>
102 </h4>
103
104 </td>
105 </tr>
106
107 </table>
108
109 <table style="text-align:left;vertical-align:middle;width: 100%">
110 ## spacing def
111 <tr>
112 <td style="width: 130px"></td>
113 <td></td>
114 </tr>
115
116 <tr>
117 <td style="padding-right:20px;">${_('Pull request')}:</td>
118 <td>
119 <a href="${pull_request_url}" style="${base.link_css()}">
120 !${pull_request.pull_request_id}
121 </a>
122 </td>
123 </tr>
124
125 <tr>
126 <td style="padding-right:20px;line-height:20px;">${_('Commit Flow')}:</td>
127 <td style="line-height:20px;">
128 <code>${'{}:{}'.format(data['source_ref_type'], pull_request.source_ref_parts.name)}</code> ${_('of')} ${data['source_repo_url']}
129 &rarr;
130 <code>${'{}:{}'.format(data['target_ref_type'], pull_request.target_ref_parts.name)}</code> ${_('of')} ${data['target_repo_url']}
131 </td>
132 </tr>
133
134 <tr>
135 <td style="padding-right:20px;">${_('Description')}:</td>
136 <td style="white-space:pre-wrap"><code>${pull_request.description | trim}</code></td>
137 </tr>
138 <tr>
139 <td style="padding-right:20px;">${_('Changes')}:</td>
140 <td style="white-space:pre-line">\
141 <strong>Changed commits:</strong>
142
143 - Added: ${len(added_commits)}
144 - Removed: ${len(removed_commits)}
145
146 <strong>Changed files:</strong>
147
148 %if not changed_files:
149 No file changes found
150 %else:
151 %for file_name in added_files:
152 - A <a href="${pull_request_url + '#a_' + h.FID(ancestor_commit_id, file_name)}">${file_name}</a>
153 %endfor
154 %for file_name in modified_files:
155 - M <a href="${pull_request_url + '#a_' + h.FID(ancestor_commit_id, file_name)}">${file_name}</a>
156 %endfor
157 %for file_name in removed_files:
158 - R <a href="${pull_request_url + '#a_' + h.FID(ancestor_commit_id, file_name)}">${file_name}</a>
159 %endfor
160 %endif
161 </td>
162 </tr>
163
164 </table>
@@ -895,7 +895,9
895
895
896 with pull_request.set_state(PullRequest.STATE_UPDATING):
896 with pull_request.set_state(PullRequest.STATE_UPDATING):
897 if PullRequestModel().has_valid_update_type(pull_request):
897 if PullRequestModel().has_valid_update_type(pull_request):
898 update_response = PullRequestModel().update_commits(pull_request)
898 db_user = apiuser.get_instance()
899 update_response = PullRequestModel().update_commits(
900 pull_request, db_user)
899 commit_changes = update_response.changes or commit_changes
901 commit_changes = update_response.changes or commit_changes
900 Session().commit()
902 Session().commit()
901
903
@@ -573,8 +573,7
573
573
574 email_kwargs = {
574 email_kwargs = {
575 'date': datetime.datetime.now(),
575 'date': datetime.datetime.now(),
576 'user': c.rhodecode_user,
576 'user': c.rhodecode_user
577 'rhodecode_version': c.rhodecode_version
578 }
577 }
579
578
580 (subject, headers, email_body,
579 (subject, headers, email_body,
@@ -78,7 +78,16
78 target_repo = AttributeDict(repo_name='repo_group/target_repo')
78 target_repo = AttributeDict(repo_name='repo_group/target_repo')
79 source_repo = AttributeDict(repo_name='repo_group/source_repo')
79 source_repo = AttributeDict(repo_name='repo_group/source_repo')
80 user = User.get_by_username(self.request.GET.get('user')) or self._rhodecode_db_user
80 user = User.get_by_username(self.request.GET.get('user')) or self._rhodecode_db_user
81
81 # file/commit changes for PR update
82 commit_changes = AttributeDict({
83 'added': ['aaaaaaabbbbb', 'cccccccddddddd'],
84 'removed': ['eeeeeeeeeee'],
85 })
86 file_changes = AttributeDict({
87 'added': ['a/file1.md', 'file2.py'],
88 'modified': ['b/modified_file.rst'],
89 'removed': ['.idea'],
90 })
82 email_kwargs = {
91 email_kwargs = {
83 'test': {},
92 'test': {},
84 'message': {
93 'message': {
@@ -87,7 +96,6
87 'email_test': {
96 'email_test': {
88 'user': user,
97 'user': user,
89 'date': datetime.datetime.now(),
98 'date': datetime.datetime.now(),
90 'rhodecode_version': c.rhodecode_version
91 },
99 },
92 'password_reset': {
100 'password_reset': {
93 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
101 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
@@ -215,6 +223,35
215
223
216 },
224 },
217
225
226 'pull_request_update': {
227 'updating_user': user,
228
229 'status_change': None,
230 'status_change_type': None,
231
232 'pull_request': pr,
233 'pull_request_commits': [],
234
235 'pull_request_target_repo': target_repo,
236 'pull_request_target_repo_url': 'http://target-repo/url',
237
238 'pull_request_source_repo': source_repo,
239 'pull_request_source_repo_url': 'http://source-repo/url',
240
241 'pull_request_url': 'http://localhost/pr1',
242
243 # update comment links
244 'pr_comment_url': 'http://comment-url',
245 'pr_comment_reply_url': 'http://comment-url#reply',
246 'ancestor_commit_id': 'f39bd443',
247 'added_commits': commit_changes.added,
248 'removed_commits': commit_changes.removed,
249 'changed_files': (file_changes.added + file_changes.modified + file_changes.removed),
250 'added_files': file_changes.added,
251 'modified_files': file_changes.modified,
252 'removed_files': file_changes.removed,
253 },
254
218 'cs_comment': {
255 'cs_comment': {
219 'user': user,
256 'user': user,
220 'commit': AttributeDict(idx=123, raw_id='a'*40, message='Commit message'),
257 'commit': AttributeDict(idx=123, raw_id='a'*40, message='Commit message'),
@@ -338,6 +375,8
338
375
339 'pull_request_comment+file': {},
376 'pull_request_comment+file': {},
340 'pull_request_comment+status': {},
377 'pull_request_comment+status': {},
378
379 'pull_request_update': {},
341 }
380 }
342 c.email_types.update(EmailNotificationModel.email_types)
381 c.email_types.update(EmailNotificationModel.email_types)
343
382
@@ -1117,7 +1117,8
1117 _ = self.request.translate
1117 _ = self.request.translate
1118
1118
1119 with pull_request.set_state(PullRequest.STATE_UPDATING):
1119 with pull_request.set_state(PullRequest.STATE_UPDATING):
1120 resp = PullRequestModel().update_commits(pull_request)
1120 resp = PullRequestModel().update_commits(
1121 pull_request, self._rhodecode_db_user)
1121
1122
1122 if resp.executed:
1123 if resp.executed:
1123
1124
@@ -821,7 +821,7
821
821
822 def discover_user(author):
822 def discover_user(author):
823 """
823 """
824 Tries to discover RhodeCode User based on the autho string. Author string
824 Tries to discover RhodeCode User based on the author string. Author string
825 is typically `FirstName LastName <email@address.com>`
825 is typically `FirstName LastName <email@address.com>`
826 """
826 """
827
827
@@ -1015,10 +1015,11
1015 #==============================================================================
1015 #==============================================================================
1016 # PERMS
1016 # PERMS
1017 #==============================================================================
1017 #==============================================================================
1018 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
1018 from rhodecode.lib.auth import (
1019 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
1019 HasPermissionAny, HasPermissionAll,
1020 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token, \
1020 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll,
1021 csrf_token_key
1021 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token,
1022 csrf_token_key, AuthUser)
1022
1023
1023
1024
1024 #==============================================================================
1025 #==============================================================================
@@ -4401,6 +4401,7
4401 TYPE_REGISTRATION = u'registration'
4401 TYPE_REGISTRATION = u'registration'
4402 TYPE_PULL_REQUEST = u'pull_request'
4402 TYPE_PULL_REQUEST = u'pull_request'
4403 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
4403 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
4404 TYPE_PULL_REQUEST_UPDATE = u'pull_request_update'
4404
4405
4405 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
4406 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
4406 subject = Column('subject', Unicode(512), nullable=True)
4407 subject = Column('subject', Unicode(512), nullable=True)
@@ -293,6 +293,7
293 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
293 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
294 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
294 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
295 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
295 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
296 TYPE_PULL_REQUEST_UPDATE = Notification.TYPE_PULL_REQUEST_UPDATE
296 TYPE_MAIN = Notification.TYPE_MESSAGE
297 TYPE_MAIN = Notification.TYPE_MESSAGE
297
298
298 TYPE_PASSWORD_RESET = 'password_reset'
299 TYPE_PASSWORD_RESET = 'password_reset'
@@ -319,6 +320,8
319 'rhodecode:templates/email_templates/pull_request_review.mako',
320 'rhodecode:templates/email_templates/pull_request_review.mako',
320 TYPE_PULL_REQUEST_COMMENT:
321 TYPE_PULL_REQUEST_COMMENT:
321 'rhodecode:templates/email_templates/pull_request_comment.mako',
322 'rhodecode:templates/email_templates/pull_request_comment.mako',
323 TYPE_PULL_REQUEST_UPDATE:
324 'rhodecode:templates/email_templates/pull_request_update.mako',
322 }
325 }
323
326
324 def __init__(self):
327 def __init__(self):
@@ -341,6 +344,7
341 """
344 """
342
345
343 kwargs['rhodecode_instance_name'] = self.rhodecode_instance_name
346 kwargs['rhodecode_instance_name'] = self.rhodecode_instance_name
347 kwargs['rhodecode_version'] = rhodecode.__version__
344 instance_url = h.route_url('home')
348 instance_url = h.route_url('home')
345 _kwargs = {
349 _kwargs = {
346 'instance_url': instance_url,
350 'instance_url': instance_url,
@@ -65,9 +65,19
65
65
66 # Data structure to hold the response data when updating commits during a pull
66 # Data structure to hold the response data when updating commits during a pull
67 # request update.
67 # request update.
68 UpdateResponse = collections.namedtuple('UpdateResponse', [
68 class UpdateResponse(object):
69 'executed', 'reason', 'new', 'old', 'changes',
69
70 'source_changed', 'target_changed'])
70 def __init__(self, executed, reason, new, old, common_ancestor_id,
71 commit_changes, source_changed, target_changed):
72
73 self.executed = executed
74 self.reason = reason
75 self.new = new
76 self.old = old
77 self.common_ancestor_id = common_ancestor_id
78 self.changes = commit_changes
79 self.source_changed = source_changed
80 self.target_changed = target_changed
71
81
72
82
73 class PullRequestModel(BaseModel):
83 class PullRequestModel(BaseModel):
@@ -672,11 +682,13
672 source_ref_type = pull_request.source_ref_parts.type
682 source_ref_type = pull_request.source_ref_parts.type
673 return source_ref_type in self.REF_TYPES
683 return source_ref_type in self.REF_TYPES
674
684
675 def update_commits(self, pull_request):
685 def update_commits(self, pull_request, updating_user):
676 """
686 """
677 Get the updated list of commits for the pull request
687 Get the updated list of commits for the pull request
678 and return the new pull request version and the list
688 and return the new pull request version and the list
679 of commits processed by this update action
689 of commits processed by this update action
690
691 updating_user is the user_object who triggered the update
680 """
692 """
681 pull_request = self.__get_pull_request(pull_request)
693 pull_request = self.__get_pull_request(pull_request)
682 source_ref_type = pull_request.source_ref_parts.type
694 source_ref_type = pull_request.source_ref_parts.type
@@ -693,7 +705,7
693 return UpdateResponse(
705 return UpdateResponse(
694 executed=False,
706 executed=False,
695 reason=UpdateFailureReason.WRONG_REF_TYPE,
707 reason=UpdateFailureReason.WRONG_REF_TYPE,
696 old=pull_request, new=None, changes=None,
708 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
697 source_changed=False, target_changed=False)
709 source_changed=False, target_changed=False)
698
710
699 # source repo
711 # source repo
@@ -705,7 +717,7
705 return UpdateResponse(
717 return UpdateResponse(
706 executed=False,
718 executed=False,
707 reason=UpdateFailureReason.MISSING_SOURCE_REF,
719 reason=UpdateFailureReason.MISSING_SOURCE_REF,
708 old=pull_request, new=None, changes=None,
720 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
709 source_changed=False, target_changed=False)
721 source_changed=False, target_changed=False)
710
722
711 source_changed = source_ref_id != source_commit.raw_id
723 source_changed = source_ref_id != source_commit.raw_id
@@ -719,7 +731,7
719 return UpdateResponse(
731 return UpdateResponse(
720 executed=False,
732 executed=False,
721 reason=UpdateFailureReason.MISSING_TARGET_REF,
733 reason=UpdateFailureReason.MISSING_TARGET_REF,
722 old=pull_request, new=None, changes=None,
734 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
723 source_changed=False, target_changed=False)
735 source_changed=False, target_changed=False)
724 target_changed = target_ref_id != target_commit.raw_id
736 target_changed = target_ref_id != target_commit.raw_id
725
737
@@ -728,7 +740,7
728 return UpdateResponse(
740 return UpdateResponse(
729 executed=False,
741 executed=False,
730 reason=UpdateFailureReason.NO_CHANGE,
742 reason=UpdateFailureReason.NO_CHANGE,
731 old=pull_request, new=None, changes=None,
743 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
732 source_changed=target_changed, target_changed=source_changed)
744 source_changed=target_changed, target_changed=source_changed)
733
745
734 change_in_found = 'target repo' if target_changed else 'source repo'
746 change_in_found = 'target repo' if target_changed else 'source repo'
@@ -759,7 +771,7
759 return UpdateResponse(
771 return UpdateResponse(
760 executed=False,
772 executed=False,
761 reason=UpdateFailureReason.MISSING_TARGET_REF,
773 reason=UpdateFailureReason.MISSING_TARGET_REF,
762 old=pull_request, new=None, changes=None,
774 old=pull_request, new=None, common_ancestor_id=None, commit_changes=None,
763 source_changed=source_changed, target_changed=target_changed)
775 source_changed=source_changed, target_changed=target_changed)
764
776
765 # re-compute commit ids
777 # re-compute commit ids
@@ -769,13 +781,13
769 target_commit.raw_id, source_commit.raw_id, source_repo, merge=True,
781 target_commit.raw_id, source_commit.raw_id, source_repo, merge=True,
770 pre_load=pre_load)
782 pre_load=pre_load)
771
783
772 ancestor = source_repo.get_common_ancestor(
784 ancestor_commit_id = source_repo.get_common_ancestor(
773 source_commit.raw_id, target_commit.raw_id, target_repo)
785 source_commit.raw_id, target_commit.raw_id, target_repo)
774
786
775 pull_request.source_ref = '%s:%s:%s' % (
787 pull_request.source_ref = '%s:%s:%s' % (
776 source_ref_type, source_ref_name, source_commit.raw_id)
788 source_ref_type, source_ref_name, source_commit.raw_id)
777 pull_request.target_ref = '%s:%s:%s' % (
789 pull_request.target_ref = '%s:%s:%s' % (
778 target_ref_type, target_ref_name, ancestor)
790 target_ref_type, target_ref_name, ancestor_commit_id)
779
791
780 pull_request.revisions = [
792 pull_request.revisions = [
781 commit.raw_id for commit in reversed(commit_ranges)]
793 commit.raw_id for commit in reversed(commit_ranges)]
@@ -787,7 +799,7
787 pull_request, pull_request_version)
799 pull_request, pull_request_version)
788
800
789 # calculate commit and file changes
801 # calculate commit and file changes
790 changes = self._calculate_commit_id_changes(
802 commit_changes = self._calculate_commit_id_changes(
791 old_commit_ids, new_commit_ids)
803 old_commit_ids, new_commit_ids)
792 file_changes = self._calculate_file_changes(
804 file_changes = self._calculate_file_changes(
793 old_diff_data, new_diff_data)
805 old_diff_data, new_diff_data)
@@ -797,23 +809,23
797 pull_request, old_diff_data=old_diff_data,
809 pull_request, old_diff_data=old_diff_data,
798 new_diff_data=new_diff_data)
810 new_diff_data=new_diff_data)
799
811
800 commit_changes = (changes.added or changes.removed)
812 valid_commit_changes = (commit_changes.added or commit_changes.removed)
801 file_node_changes = (
813 file_node_changes = (
802 file_changes.added or file_changes.modified or file_changes.removed)
814 file_changes.added or file_changes.modified or file_changes.removed)
803 pr_has_changes = commit_changes or file_node_changes
815 pr_has_changes = valid_commit_changes or file_node_changes
804
816
805 # Add an automatic comment to the pull request, in case
817 # Add an automatic comment to the pull request, in case
806 # anything has changed
818 # anything has changed
807 if pr_has_changes:
819 if pr_has_changes:
808 update_comment = CommentsModel().create(
820 update_comment = CommentsModel().create(
809 text=self._render_update_message(changes, file_changes),
821 text=self._render_update_message(ancestor_commit_id, commit_changes, file_changes),
810 repo=pull_request.target_repo,
822 repo=pull_request.target_repo,
811 user=pull_request.author,
823 user=pull_request.author,
812 pull_request=pull_request,
824 pull_request=pull_request,
813 send_email=False, renderer=DEFAULT_COMMENTS_RENDERER)
825 send_email=False, renderer=DEFAULT_COMMENTS_RENDERER)
814
826
815 # Update status to "Under Review" for added commits
827 # Update status to "Under Review" for added commits
816 for commit_id in changes.added:
828 for commit_id in commit_changes.added:
817 ChangesetStatusModel().set_status(
829 ChangesetStatusModel().set_status(
818 repo=pull_request.source_repo,
830 repo=pull_request.source_repo,
819 status=ChangesetStatus.STATUS_UNDER_REVIEW,
831 status=ChangesetStatus.STATUS_UNDER_REVIEW,
@@ -822,10 +834,19
822 pull_request=pull_request,
834 pull_request=pull_request,
823 revision=commit_id)
835 revision=commit_id)
824
836
837 # send update email to users
838 try:
839 self.notify_users(pull_request=pull_request, updating_user=updating_user,
840 ancestor_commit_id=ancestor_commit_id,
841 commit_changes=commit_changes,
842 file_changes=file_changes)
843 except Exception:
844 log.exception('Failed to send email notification to users')
845
825 log.debug(
846 log.debug(
826 'Updated pull request %s, added_ids: %s, common_ids: %s, '
847 'Updated pull request %s, added_ids: %s, common_ids: %s, '
827 'removed_ids: %s', pull_request.pull_request_id,
848 'removed_ids: %s', pull_request.pull_request_id,
828 changes.added, changes.common, changes.removed)
849 commit_changes.added, commit_changes.common, commit_changes.removed)
829 log.debug(
850 log.debug(
830 'Updated pull request with the following file changes: %s',
851 'Updated pull request with the following file changes: %s',
831 file_changes)
852 file_changes)
@@ -841,7 +862,8
841
862
842 return UpdateResponse(
863 return UpdateResponse(
843 executed=True, reason=UpdateFailureReason.NONE,
864 executed=True, reason=UpdateFailureReason.NONE,
844 old=pull_request, new=pull_request_version, changes=changes,
865 old=pull_request, new=pull_request_version,
866 common_ancestor_id=ancestor_commit_id, commit_changes=commit_changes,
845 source_changed=source_changed, target_changed=target_changed)
867 source_changed=source_changed, target_changed=target_changed)
846
868
847 def _create_version_from_snapshot(self, pull_request):
869 def _create_version_from_snapshot(self, pull_request):
@@ -963,12 +985,13
963
985
964 return FileChangeTuple(added_files, modified_files, removed_files)
986 return FileChangeTuple(added_files, modified_files, removed_files)
965
987
966 def _render_update_message(self, changes, file_changes):
988 def _render_update_message(self, ancestor_commit_id, changes, file_changes):
967 """
989 """
968 render the message using DEFAULT_COMMENTS_RENDERER (RST renderer),
990 render the message using DEFAULT_COMMENTS_RENDERER (RST renderer),
969 so it's always looking the same disregarding on which default
991 so it's always looking the same disregarding on which default
970 renderer system is using.
992 renderer system is using.
971
993
994 :param ancestor_commit_id: ancestor raw_id
972 :param changes: changes named tuple
995 :param changes: changes named tuple
973 :param file_changes: file changes named tuple
996 :param file_changes: file changes named tuple
974
997
@@ -987,6 +1010,7
987 'added_files': file_changes.added,
1010 'added_files': file_changes.added,
988 'modified_files': file_changes.modified,
1011 'modified_files': file_changes.modified,
989 'removed_files': file_changes.removed,
1012 'removed_files': file_changes.removed,
1013 'ancestor_commit_id': ancestor_commit_id
990 }
1014 }
991 renderer = RstTemplateRenderer()
1015 renderer = RstTemplateRenderer()
992 return renderer.render('pull_request_update.mako', **params)
1016 return renderer.render('pull_request_update.mako', **params)
@@ -1170,6 +1194,75
1170 email_kwargs=kwargs,
1194 email_kwargs=kwargs,
1171 )
1195 )
1172
1196
1197 def notify_users(self, pull_request, updating_user, ancestor_commit_id,
1198 commit_changes, file_changes):
1199
1200 updating_user_id = updating_user.user_id
1201 reviewers = set([x.user.user_id for x in pull_request.reviewers])
1202 # NOTE(marcink): send notification to all other users except to
1203 # person who updated the PR
1204 recipients = reviewers.difference(set([updating_user_id]))
1205
1206 log.debug('Notify following recipients about pull-request update %s', recipients)
1207
1208 pull_request_obj = pull_request
1209
1210 # send email about the update
1211 changed_files = (
1212 file_changes.added + file_changes.modified + file_changes.removed)
1213
1214 pr_source_repo = pull_request_obj.source_repo
1215 pr_target_repo = pull_request_obj.target_repo
1216
1217 pr_url = h.route_url('pullrequest_show',
1218 repo_name=pr_target_repo.repo_name,
1219 pull_request_id=pull_request_obj.pull_request_id,)
1220
1221 # set some variables for email notification
1222 pr_target_repo_url = h.route_url(
1223 'repo_summary', repo_name=pr_target_repo.repo_name)
1224
1225 pr_source_repo_url = h.route_url(
1226 'repo_summary', repo_name=pr_source_repo.repo_name)
1227
1228 email_kwargs = {
1229 'date': datetime.datetime.now(),
1230 'updating_user': updating_user,
1231
1232 'pull_request': pull_request_obj,
1233
1234 'pull_request_target_repo': pr_target_repo,
1235 'pull_request_target_repo_url': pr_target_repo_url,
1236
1237 'pull_request_source_repo': pr_source_repo,
1238 'pull_request_source_repo_url': pr_source_repo_url,
1239
1240 'pull_request_url': pr_url,
1241
1242 'ancestor_commit_id': ancestor_commit_id,
1243 'added_commits': commit_changes.added,
1244 'removed_commits': commit_changes.removed,
1245 'changed_files': changed_files,
1246 'added_files': file_changes.added,
1247 'modified_files': file_changes.modified,
1248 'removed_files': file_changes.removed,
1249 }
1250
1251 (subject,
1252 _h, _e, # we don't care about those
1253 body_plaintext) = EmailNotificationModel().render_email(
1254 EmailNotificationModel.TYPE_PULL_REQUEST_UPDATE, **email_kwargs)
1255
1256 # create notification objects, and emails
1257 NotificationModel().create(
1258 created_by=updating_user,
1259 notification_subject=subject,
1260 notification_body=body_plaintext,
1261 notification_type=EmailNotificationModel.TYPE_PULL_REQUEST_UPDATE,
1262 recipients=recipients,
1263 email_kwargs=email_kwargs,
1264 )
1265
1173 def delete(self, pull_request, user):
1266 def delete(self, pull_request, user):
1174 pull_request = self.__get_pull_request(pull_request)
1267 pull_request = self.__get_pull_request(pull_request)
1175 old_data = pull_request.get_api_data(with_merge_state=False)
1268 old_data = pull_request.get_api_data(with_merge_state=False)
@@ -115,17 +115,17
115 <td style="width:100%;border-bottom:1px solid #dbd9da;">
115 <td style="width:100%;border-bottom:1px solid #dbd9da;">
116
116
117 <h4 style="margin: 0">
117 <h4 style="margin: 0">
118 <div style="margin-bottom: 4px; color:#7E7F7F">
118 <div style="margin-bottom: 4px">
119 @${h.person(user.username)}
119 <span style="color:#7E7F7F">@${h.person(user.username)}</span>
120 ${_('left a')}
121 <a href="${pr_comment_url}" style="${base.link_css()}">
122 % if comment_file:
123 ${_('{comment_type} on file `{comment_file}` in pull request.').format(**data)}
124 % else:
125 ${_('{comment_type} on pull request.').format(**data) |n}
126 % endif
127 </a>
120 </div>
128 </div>
121 ${_('left a')}
122 <a href="${pr_comment_url}" style="${base.link_css()}">
123 % if comment_file:
124 ${_('{comment_type} on file `{comment_file}` in pull request.').format(**data)}
125 % else:
126 ${_('{comment_type} on pull request.').format(**data) |n}
127 % endif
128 </a>
129 <div style="margin-top: 10px"></div>
129 <div style="margin-top: 10px"></div>
130 ${_('Pull request')} <code>!${data['pr_id']}: ${data['pr_title']}</code>
130 ${_('Pull request')} <code>!${data['pr_id']}: ${data['pr_title']}</code>
131 </h4>
131 </h4>
@@ -78,13 +78,13
78 <td style="width:100%;border-bottom:1px solid #dbd9da;">
78 <td style="width:100%;border-bottom:1px solid #dbd9da;">
79
79
80 <h4 style="margin: 0">
80 <h4 style="margin: 0">
81 <div style="margin-bottom: 4px; color:#7E7F7F">
81 <div style="margin-bottom: 4px">
82 @${h.person(user.username)}
82 <span style="color:#7E7F7F">@${h.person(user.username)}</span>