def get_participants_for_release( projects: Iterable[Project], organization: Organization, user_ids: Set[int]) -> Mapping[ExternalProviders, Mapping[User, int]]: # Collect all users with verified emails on a team in the related projects. users = list( User.objects.get_team_members_with_verified_email_for_projects( projects)) # Get all the involved users' settings for deploy-emails (including # users' organization-independent settings.) notification_settings = NotificationSetting.objects.get_for_users_by_parent( NotificationSettingTypes.DEPLOY, users=users, parent=organization, ) notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, users) # Map users to their setting value. Prioritize user/org specific, then # user default, then product default. users_to_reasons_by_provider: MutableMapping[ ExternalProviders, MutableMapping[User, int]] = defaultdict(dict) for user in users: notification_settings_by_scope = notification_settings_by_user.get( user, {}) values_by_provider = get_deploy_values_by_provider( notification_settings_by_scope, notification_providers()) for provider, value in values_by_provider.items(): reason_option = get_reason(user, value, user_ids) if reason_option: users_to_reasons_by_provider[provider][user] = reason_option return users_to_reasons_by_provider
def get_participants(self, group) -> Mapping[Any, GroupSubscriptionReason]: """ Identify all users who are participating with a given issue. :param group: Group object """ from sentry.models import NotificationSetting, User users = User.objects.get_from_group(group) user_ids = [user.id for user in users] subscriptions = self.filter(group=group, user_id__in=user_ids) notification_settings = NotificationSetting.objects.get_for_users_by_parent( ExternalProviders.EMAIL, NotificationSettingTypes.WORKFLOW, users=users, parent=group.project, ) subscriptions_by_user_id = { subscription.user_id: subscription for subscription in subscriptions } notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, users) return { user: getattr( subscriptions_by_user_id.get(user.id), "reason", GroupSubscriptionReason.implicit, ) for user in users if should_be_participating( user, subscriptions_by_user_id, notification_settings_by_user, ) }
def disabled_users_from_project( project: Project) -> Mapping[ExternalProviders, Set[User]]: """ Get a set of users that have disabled Issue Alert notifications for a given project. """ user_ids = project.member_set.values_list("user", flat=True) users = User.objects.filter(id__in=user_ids) notification_settings = NotificationSetting.objects.get_for_users_by_parent( type=NotificationSettingTypes.ISSUE_ALERTS, parent=project, users=users, ) notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, users) # Although this can be done with dict comprehension, looping for clarity. output = defaultdict(set) for user in users: settings = notification_settings_by_user.get(user) if settings: settings_by_provider = get_settings_by_provider(settings) for provider, settings_value_by_scope in settings_by_provider.items( ): project_setting = settings_value_by_scope.get( NotificationScopeType.PROJECT) user_setting = settings_value_by_scope.get( NotificationScopeType.USER) if project_setting == NotificationSettingOptionValues.NEVER or ( not project_setting and user_setting == NotificationSettingOptionValues.NEVER): output[provider].add(user) return output
def filter_to_subscribed_users( self, project: "Project", users: List["User"], ) -> Mapping[ExternalProviders, Iterable["User"]]: """ Filters a list of users down to the users by provider who are subscribed to alerts. We check both the project level settings and global default settings. """ notification_settings = self.get_for_recipient_by_parent( NotificationSettingTypes.ISSUE_ALERTS, parent=project, recipients=users) notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, users) mapping = defaultdict(set) should_use_slack_automatic = features.has( "organizations:notification-slack-automatic", project.organization) for user in users: providers = where_should_user_be_notified( notification_settings_by_user, user, should_use_slack_automatic) for provider in providers: mapping[provider].add(user) return mapping
def disabled_users_from_project(project: Project) -> Set[int]: """ Get a set of users that have disabled Issue Alert notifications for a given project. """ user_ids = project.member_set.values_list("user", flat=True) users = User.objects.filter(id__in=user_ids) notification_settings = NotificationSetting.objects.get_for_users_by_parent( type=NotificationSettingTypes.ISSUE_ALERTS, parent=project, users=users, ) notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, users) # Although this can be done with dict comprehension, looping for clarity. output = set() for user in users: settings = notification_settings_by_user.get(user) if settings: # Check per-project settings first, fallback to project-independent settings. project_setting = settings.get(NotificationScopeType.PROJECT) if project_setting: project_setting = project_setting[ExternalProviders.EMAIL] user_setting = settings.get(NotificationScopeType.USER) if user_setting: user_setting = user_setting[ExternalProviders.EMAIL] if project_setting == NotificationSettingOptionValues.NEVER or ( not project_setting and user_setting == NotificationSettingOptionValues.NEVER): output.add(user.id) return output
def test_transform_to_notification_settings_by_user(self): assert transform_to_notification_settings_by_user( notification_settings=self.notification_settings, users=[self.user]) == { self.user: { NotificationScopeType.USER: { ExternalProviders.SLACK: NotificationSettingOptionValues.ALWAYS }, NotificationScopeType.PROJECT: { ExternalProviders.SLACK: NotificationSettingOptionValues.ALWAYS }, } }
def test_transform_to_notification_settings_by_user(self): notification_settings = NotificationSetting.objects.get_for_users_by_parent( NotificationSettingTypes.WORKFLOW, users=[self.user], parent=self.group.project, ) notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, [self.user]) assert notification_settings_by_user == { self.user: { NotificationScopeType.USER: { ExternalProviders.SLACK: NotificationSettingOptionValues.ALWAYS } } }
def get_participants( self, group: "Group" ) -> Mapping[ExternalProviders, Mapping["User", int]]: """ Identify all users who are participating with a given issue. :param group: Group object """ from sentry.models import NotificationSetting, User all_possible_users = User.objects.get_from_group(group) active_and_disabled_subscriptions = self.filter( group=group, user__in=all_possible_users) notification_settings = NotificationSetting.objects.get_for_recipient_by_parent( NotificationSettingTypes.WORKFLOW, recipients=all_possible_users, parent=group.project, ) subscriptions_by_user_id = { subscription.user_id: subscription for subscription in active_and_disabled_subscriptions } notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, all_possible_users) should_use_slack_automatic = features.has( "organizations:notification-slack-automatic", group.organization) result: MutableMapping[ExternalProviders, MutableMapping["User", int]] = defaultdict(dict) for user in all_possible_users: subscription_option = subscriptions_by_user_id.get(user.id) providers = where_should_be_participating( user, subscription_option, notification_settings_by_user, should_use_slack_automatic=should_use_slack_automatic, ) for provider in providers: result[provider][user] = (subscription_option and subscription_option.reason or GroupSubscriptionReason.implicit) return result
def filter_to_subscribed_users( self, project: Any, users: List[Any], ) -> Mapping[ExternalProviders, Iterable[Any]]: """ Filters a list of users down to the users by provider who are subscribed to alerts. We check both the project level settings and global default settings. """ notification_settings = self.get_for_users_by_parent( NotificationSettingTypes.ISSUE_ALERTS, parent=project, users=users) notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, users) mapping = defaultdict(set) for user in users: providers = where_should_user_be_notified( notification_settings_by_user, user) for provider in providers: mapping[provider].add(user) return mapping
def filter_to_subscribed_users( self, provider: ExternalProviders, project: Any, users: List[Any], ) -> List[Any]: """ Filters a list of users down to the users who are subscribed to email alerts. We check both the project level settings and global default settings. """ notification_settings = self.get_for_users_by_parent( provider, NotificationSettingTypes.ISSUE_ALERTS, parent=project, users=users) notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, users) return [ user for user in users if should_user_be_notified(notification_settings_by_user, user) ]
def get_participants( self, group) -> Mapping[ExternalProviders, Mapping[Any, int]]: """ Identify all users who are participating with a given issue. :param group: Group object """ from sentry.models import NotificationSetting, User users = User.objects.get_from_group(group) user_ids = [user.id for user in users] subscriptions = self.filter(group=group, user_id__in=user_ids) notification_settings = NotificationSetting.objects.get_for_recipient_by_parent( NotificationSettingTypes.WORKFLOW, recipients=users, parent=group.project, ) subscriptions_by_user_id = { subscription.user_id: subscription for subscription in subscriptions } notification_settings_by_user = transform_to_notification_settings_by_user( notification_settings, users) result = defaultdict(dict) for user in users: providers = where_should_be_participating( user, subscriptions_by_user_id, notification_settings_by_user, ) for provider in providers: value = getattr( subscriptions_by_user_id.get(user.id), "reason", GroupSubscriptionReason.implicit, ) result[provider][user] = value return result
def test_transform_to_notification_settings_by_user_empty(self): assert transform_to_notification_settings_by_user( notification_settings=[], users=[]) == {} assert (transform_to_notification_settings_by_user( notification_settings=[], users=[self.user]) == {})