def testComputeGroupReasonList_NotifyAll(self): """Project is configured to always notify [email protected].""" self.project.issue_notify_address = '*****@*****.**' actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)], all_notifications_apl=[notify_reasons.AddrPerm( False, '*****@*****.**', None, REPLY_NOT_ALLOWED)]) # We don't use the notify-all address when the issue is not public. actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], False) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)]) # Now with the notify-all address disabled. actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True, include_notify_all=False) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)])
def testComputeGroupReasonList_Subscribers(self): """Bob subscribed.""" sq = tracker_bizobj.MakeSavedQuery( 1, 'freds issues', 1, 'owner:[email protected]', subscription_mode='immediate', executes_in_project_ids=[789]) self.services.features.UpdateUserSavedQueries( 'cnxn', self.bob.user_id, [sq]) actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)], subscriber_apl=[notify_reasons.AddrPerm( False, self.bob.email, self.bob, REPLY_NOT_ALLOWED)]) # Now with subscriber notifications disabled. actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True, include_subscribers=False) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)])
def _MakeEmailTasks(self, cnxn, issue, project, config, comment, hostport, users_by_id, pings): """Return a list of dicts for tasks to notify people.""" detail_url = framework_helpers.IssueCommentURL( hostport, project, issue.local_id, seq_num=comment.sequence) fields = sorted((field_def for (field_def, _date_value) in pings), key=lambda fd: fd.field_name) email_data = { 'issue': tracker_views.IssueView(issue, users_by_id, config), 'summary': issue.summary, 'ping_comment_content': comment.content, 'detail_url': detail_url, 'fields': fields, } # Generate two versions of email body: members version has all # full email addresses exposed. body_for_non_members = self.email_template.GetResponse(email_data) framework_views.RevealAllEmails(users_by_id) body_for_members = self.email_template.GetResponse(email_data) logging.info('body for non-members is:\n%r' % body_for_non_members) logging.info('body for members is:\n%r' % body_for_members) contributor_could_view = permissions.CanViewIssue( set(), permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET, project, issue) # Note: We never notify the reporter of any issue just because they # reported it, only if they star it. # TODO(jrobbins): add a user preference for notifying starrers. starrer_ids = [] # TODO(jrobbins): consider IsNoisy() when we support notifying starrers. group_reason_list = notify_reasons.ComputeGroupReasonList( cnxn, self.services, project, issue, config, users_by_id, [], contributor_could_view, starrer_ids=starrer_ids, commenter_in_project=True) commenter_view = users_by_id[comment.user_id] email_tasks = notify_helpers.MakeBulletedEmailWorkItems( group_reason_list, issue, body_for_non_members, body_for_members, project, hostport, commenter_view, detail_url, seq_num=comment.sequence, subject_prefix='Follow up on issue ', compact_subject_prefix='Follow up ') return email_tasks
def _ProcessUpstreamIssue( self, cnxn, upstream_issue, upstream_project, upstream_config, issue, omit_ids, hostport, commenter_view): """Compute notifications for one upstream issue that is now blocking.""" upstream_detail_url = framework_helpers.FormatAbsoluteURLForDomain( hostport, upstream_issue.project_name, urls.ISSUE_DETAIL, id=upstream_issue.local_id) logging.info('upstream_detail_url = %r', upstream_detail_url) detail_url = framework_helpers.FormatAbsoluteURLForDomain( hostport, issue.project_name, urls.ISSUE_DETAIL, id=issue.local_id) # Only issues that any contributor could view are sent to mailing lists. contributor_could_view = permissions.CanViewIssue( set(), permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET, upstream_project, upstream_issue) # Now construct the e-mail to send # Note: we purposely do not notify users who starred an issue # about changes in blocking. users_by_id = framework_views.MakeAllUserViews( cnxn, self.services.user, tracker_bizobj.UsersInvolvedInIssues([upstream_issue]), omit_ids) is_blocking = upstream_issue.issue_id in issue.blocked_on_iids email_data = { 'issue': tracker_views.IssueView( upstream_issue, users_by_id, upstream_config), 'summary': upstream_issue.summary, 'detail_url': upstream_detail_url, 'is_blocking': ezt.boolean(is_blocking), 'downstream_issue_ref': tracker_bizobj.FormatIssueRef( (None, issue.local_id)), 'downstream_issue_url': detail_url, } # TODO(jrobbins): Generate two versions of email body: members # vesion has other member full email addresses exposed. But, don't # expose too many as we iterate through upstream projects. body_link_only = self.link_only_email_template.GetResponse( {'detail_url': upstream_detail_url, 'was_created': ezt.boolean(False)}) body = self.email_template.GetResponse(email_data) omit_addrs = {users_by_id[omit_id].email for omit_id in omit_ids} # Get the transitive set of owners and Cc'd users, and their UserView's. # Give each user a bullet-list of all the reasons that apply for that user. # Starrers are not notified of blocking changes to reduce noise. group_reason_list = notify_reasons.ComputeGroupReasonList( cnxn, self.services, upstream_project, upstream_issue, upstream_config, users_by_id, omit_addrs, contributor_could_view) one_issue_email_tasks = notify_helpers.MakeBulletedEmailWorkItems( group_reason_list, upstream_issue, body_link_only, body, body, upstream_project, hostport, commenter_view, detail_url) return one_issue_email_tasks
def testComputeGroupReasonList_OwnerAndCC(self): """Fred owns the issue, Alice is CC'd.""" self.issue.cc_ids = [self.alice.user_id] actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)], ccd_apl=[notify_reasons.AddrPerm( False, self.alice.email, self.alice, REPLY_NOT_ALLOWED)])
def testComputeGroupReasonList_Starrers(self): """Bob and Alice starred it, but Alice opts out of notifications.""" self.alice.notify_starred_issue_change = False actual = notify_reasons.ComputeGroupReasonList( 'cnxn', self.services, self.project, self.issue, self.config, self.users_by_id, [], True, starrer_ids=[self.alice.user_id, self.bob.user_id]) self.CheckGroupReasonList( actual, owner_apl=[notify_reasons.AddrPerm( False, self.fred.email, self.fred, REPLY_NOT_ALLOWED)], starrer_apl=[notify_reasons.AddrPerm( False, self.bob.email, self.bob, REPLY_NOT_ALLOWED)])
def _MakeEmailTasks( self, cnxn, project, issue, config, old_owner_id, users_by_id, all_comments, comment, starrer_ids, contributor_could_view, hostport, omit_ids, perms): """Formulate emails to be sent.""" detail_url = framework_helpers.IssueCommentURL( hostport, project, issue.local_id, seq_num=comment.sequence) # TODO(jrobbins): avoid the need to make a MonorailRequest object. mr = monorailrequest.MonorailRequest(self.services) mr.project_name = project.project_name mr.project = project mr.perms = perms # We do not autolink in the emails, so just use an empty # registry of autolink rules. # TODO(jrobbins): offer users an HTML email option w/ autolinks. autolinker = autolink.Autolink() was_created = ezt.boolean(comment.sequence == 0) email_data = { # Pass open_related and closed_related into this method and to # the issue view so that we can show it on new issue email. 'issue': tracker_views.IssueView(issue, users_by_id, config), 'summary': issue.summary, 'comment': tracker_views.IssueCommentView( project.project_name, comment, users_by_id, autolinker, {}, mr, issue), 'comment_text': comment.content, 'detail_url': detail_url, 'was_created': was_created, } # Generate three versions of email body: link-only is just the link, # non-members see some obscured email addresses, and members version has # all full email addresses exposed. body_link_only = self.link_only_email_template.GetResponse( {'detail_url': detail_url, 'was_created': was_created}) body_for_non_members = self.email_template.GetResponse(email_data) framework_views.RevealAllEmails(users_by_id) email_data['comment'] = tracker_views.IssueCommentView( project.project_name, comment, users_by_id, autolinker, {}, mr, issue) body_for_members = self.email_template.GetResponse(email_data) logging.info('link-only body is:\n%r' % body_link_only) logging.info('body for non-members is:\n%r' % body_for_non_members) logging.info('body for members is:\n%r' % body_for_members) commenter_email = users_by_id[comment.user_id].email omit_addrs = set([commenter_email] + [users_by_id[omit_id].email for omit_id in omit_ids]) auth = authdata.AuthData.FromUserID( cnxn, comment.user_id, self.services) commenter_in_project = framework_bizobj.UserIsInProject( project, auth.effective_ids) noisy = tracker_helpers.IsNoisy(len(all_comments) - 1, len(starrer_ids)) # Give each user a bullet-list of all the reasons that apply for that user. group_reason_list = notify_reasons.ComputeGroupReasonList( cnxn, self.services, project, issue, config, users_by_id, omit_addrs, contributor_could_view, noisy=noisy, starrer_ids=starrer_ids, old_owner_id=old_owner_id, commenter_in_project=commenter_in_project) commenter_view = users_by_id[comment.user_id] detail_url = framework_helpers.FormatAbsoluteURLForDomain( hostport, issue.project_name, urls.ISSUE_DETAIL, id=issue.local_id) email_tasks = notify_helpers.MakeBulletedEmailWorkItems( group_reason_list, issue, body_link_only, body_for_non_members, body_for_members, project, hostport, commenter_view, detail_url, seq_num=comment.sequence) return email_tasks
def _MakeEmailTasks(self, cnxn, issue, project, config, comment, starrer_ids, hostport, users_by_id, pings): """Return a list of dicts for tasks to notify people.""" detail_url = framework_helpers.IssueCommentURL( hostport, project, issue.local_id, seq_num=comment.sequence) fields = sorted((field_def for (field_def, _date_value) in pings), key=lambda fd: fd.field_name) email_data = { 'issue': tracker_views.IssueView(issue, users_by_id, config), 'summary': issue.summary, 'ping_comment_content': comment.content, 'detail_url': detail_url, 'fields': fields, } # Generate three versions of email body with progressively more info. body_link_only = self.link_only_email_template.GetResponse({ 'detail_url': detail_url, 'was_created': ezt.boolean(False) }) body_for_non_members = self.email_template.GetResponse(email_data) framework_views.RevealAllEmails(users_by_id) body_for_members = self.email_template.GetResponse(email_data) logging.info('body for non-members is:\n%r' % body_for_non_members) logging.info('body for members is:\n%r' % body_for_members) contributor_could_view = permissions.CanViewIssue( set(), permissions.CONTRIBUTOR_ACTIVE_PERMISSIONSET, project, issue) group_reason_list = notify_reasons.ComputeGroupReasonList( cnxn, self.services, project, issue, config, users_by_id, [], contributor_could_view, starrer_ids=starrer_ids, commenter_in_project=True, include_subscribers=False, include_notify_all=False, starrer_pref_check_function=lambda u: u.notify_starred_ping) commenter_view = users_by_id[comment.user_id] email_tasks = notify_helpers.MakeBulletedEmailWorkItems( group_reason_list, issue, body_link_only, body_for_non_members, body_for_members, project, hostport, commenter_view, detail_url, seq_num=comment.sequence, subject_prefix='Follow up on issue ', compact_subject_prefix='Follow up ') return email_tasks