def send_subscription_add_events( realm: Realm, sub_info_list: List[SubInfo], subscriber_dict: Dict[int, Set[int]], ) -> None: info_by_user: Dict[int, List[SubInfo]] = defaultdict(list) for sub_info in sub_info_list: info_by_user[sub_info.user.id].append(sub_info) stream_ids = {sub_info.stream.id for sub_info in sub_info_list} recent_traffic = get_streams_traffic(stream_ids=stream_ids) # We generally only have a few streams, so we compute stream # data in its own loop. stream_info_dict: Dict[int, StreamInfo] = {} for sub_info in sub_info_list: stream = sub_info.stream if stream.id not in stream_info_dict: email_address = encode_email_address(stream, show_sender=True) stream_weekly_traffic = get_average_weekly_stream_traffic( stream.id, stream.date_created, recent_traffic ) if stream.is_in_zephyr_realm and not stream.invite_only: subscribers = [] else: subscribers = list(subscriber_dict[stream.id]) stream_info_dict[stream.id] = StreamInfo( email_address=email_address, stream_weekly_traffic=stream_weekly_traffic, subscribers=subscribers, ) for user_id, sub_infos in info_by_user.items(): sub_dicts = [] for sub_info in sub_infos: stream = sub_info.stream stream_info = stream_info_dict[stream.id] subscription = sub_info.sub sub_dict = stream.to_dict() for field_name in Subscription.API_FIELDS: sub_dict[field_name] = getattr(subscription, field_name) sub_dict["in_home_view"] = not subscription.is_muted sub_dict["email_address"] = stream_info.email_address sub_dict["stream_weekly_traffic"] = stream_info.stream_weekly_traffic sub_dict["subscribers"] = stream_info.subscribers sub_dicts.append(sub_dict) # Send a notification to the user who subscribed. event = dict(type="subscription", op="add", subscriptions=sub_dicts) send_event(realm, event, [user_id])
def build_stream_dict_for_never_sub( raw_stream_dict: RawStreamDict, recent_traffic: Dict[int, int], ) -> NeverSubscribedStreamDict: date_created = datetime_to_timestamp(raw_stream_dict["date_created"]) description = raw_stream_dict["description"] first_message_id = raw_stream_dict["first_message_id"] history_public_to_subscribers = raw_stream_dict[ "history_public_to_subscribers"] invite_only = raw_stream_dict["invite_only"] is_web_public = raw_stream_dict["is_web_public"] message_retention_days = raw_stream_dict["message_retention_days"] name = raw_stream_dict["name"] rendered_description = raw_stream_dict["rendered_description"] stream_id = raw_stream_dict["id"] stream_post_policy = raw_stream_dict["stream_post_policy"] stream_weekly_traffic = get_average_weekly_stream_traffic( raw_stream_dict["id"], raw_stream_dict["date_created"], recent_traffic) # Backwards-compatibility addition of removed field. is_announcement_only = raw_stream_dict[ "stream_post_policy"] == Stream.STREAM_POST_POLICY_ADMINS # Our caller may add a subscribers field. return NeverSubscribedStreamDict( date_created=date_created, description=description, first_message_id=first_message_id, history_public_to_subscribers=history_public_to_subscribers, invite_only=invite_only, is_announcement_only=is_announcement_only, is_web_public=is_web_public, message_retention_days=message_retention_days, name=name, rendered_description=rendered_description, stream_id=stream_id, stream_post_policy=stream_post_policy, stream_weekly_traffic=stream_weekly_traffic, )
def build_stream_dict_for_sub( user: UserProfile, sub_dict: RawSubscriptionDict, raw_stream_dict: RawStreamDict, recent_traffic: Dict[int, int], ) -> SubscriptionStreamDict: # Handle Stream.API_FIELDS date_created = datetime_to_timestamp(raw_stream_dict["date_created"]) description = raw_stream_dict["description"] first_message_id = raw_stream_dict["first_message_id"] history_public_to_subscribers = raw_stream_dict[ "history_public_to_subscribers"] invite_only = raw_stream_dict["invite_only"] is_web_public = raw_stream_dict["is_web_public"] message_retention_days = raw_stream_dict["message_retention_days"] name = raw_stream_dict["name"] rendered_description = raw_stream_dict["rendered_description"] stream_id = raw_stream_dict["id"] stream_post_policy = raw_stream_dict["stream_post_policy"] # Handle Subscription.API_FIELDS. color = sub_dict["color"] is_muted = sub_dict["is_muted"] pin_to_top = sub_dict["pin_to_top"] audible_notifications = sub_dict["audible_notifications"] desktop_notifications = sub_dict["desktop_notifications"] email_notifications = sub_dict["email_notifications"] push_notifications = sub_dict["push_notifications"] wildcard_mentions_notify = sub_dict["wildcard_mentions_notify"] role = sub_dict["role"] # Backwards-compatibility for clients that haven't been # updated for the in_home_view => is_muted API migration. in_home_view = not is_muted # Backwards-compatibility for clients that haven't been # updated for the is_announcement_only -> stream_post_policy # migration. is_announcement_only = raw_stream_dict[ "stream_post_policy"] == Stream.STREAM_POST_POLICY_ADMINS # Add a few computed fields not directly from the data models. stream_weekly_traffic = get_average_weekly_stream_traffic( raw_stream_dict["id"], raw_stream_dict["date_created"], recent_traffic) email_address = encode_email_address_helper(raw_stream_dict["name"], raw_stream_dict["email_token"], show_sender=True) # Our caller may add a subscribers field. return SubscriptionStreamDict( audible_notifications=audible_notifications, color=color, date_created=date_created, description=description, desktop_notifications=desktop_notifications, email_address=email_address, email_notifications=email_notifications, first_message_id=first_message_id, history_public_to_subscribers=history_public_to_subscribers, in_home_view=in_home_view, invite_only=invite_only, is_announcement_only=is_announcement_only, is_muted=is_muted, is_web_public=is_web_public, message_retention_days=message_retention_days, name=name, pin_to_top=pin_to_top, push_notifications=push_notifications, rendered_description=rendered_description, role=role, stream_id=stream_id, stream_post_policy=stream_post_policy, stream_weekly_traffic=stream_weekly_traffic, wildcard_mentions_notify=wildcard_mentions_notify, )
def get_web_public_subs(realm: Realm) -> SubscriptionInfo: color_idx = 0 def get_next_color() -> str: nonlocal color_idx color = STREAM_ASSIGNMENT_COLORS[color_idx] color_idx = (color_idx + 1) % len(STREAM_ASSIGNMENT_COLORS) return color subscribed = [] for stream in get_web_public_streams_queryset(realm): # Add Stream fields. date_created = datetime_to_timestamp(stream.date_created) description = stream.description first_message_id = stream.first_message_id history_public_to_subscribers = stream.history_public_to_subscribers invite_only = stream.invite_only is_announcement_only = stream.stream_post_policy == Stream.STREAM_POST_POLICY_ADMINS is_web_public = stream.is_web_public message_retention_days = stream.message_retention_days name = stream.name rendered_description = stream.rendered_description stream_id = stream.id stream_post_policy = stream.stream_post_policy # Add versions of the Subscription fields based on a simulated # new user subscription set. audible_notifications = True color = get_next_color() desktop_notifications = True email_address = "" email_notifications = True in_home_view = True is_muted = False pin_to_top = False push_notifications = True role = Subscription.ROLE_MEMBER stream_weekly_traffic = get_average_weekly_stream_traffic( stream.id, stream.date_created, {}) wildcard_mentions_notify = True sub = SubscriptionStreamDict( audible_notifications=audible_notifications, color=color, date_created=date_created, description=description, desktop_notifications=desktop_notifications, email_address=email_address, email_notifications=email_notifications, first_message_id=first_message_id, history_public_to_subscribers=history_public_to_subscribers, in_home_view=in_home_view, invite_only=invite_only, is_announcement_only=is_announcement_only, is_muted=is_muted, is_web_public=is_web_public, message_retention_days=message_retention_days, name=name, pin_to_top=pin_to_top, push_notifications=push_notifications, rendered_description=rendered_description, role=role, stream_id=stream_id, stream_post_policy=stream_post_policy, stream_weekly_traffic=stream_weekly_traffic, wildcard_mentions_notify=wildcard_mentions_notify, ) subscribed.append(sub) return SubscriptionInfo( subscriptions=subscribed, unsubscribed=[], never_subscribed=[], )
def send_subscription_add_events( realm: Realm, sub_info_list: List[SubInfo], subscriber_dict: Dict[int, Set[int]], ) -> None: info_by_user: Dict[int, List[SubInfo]] = defaultdict(list) for sub_info in sub_info_list: info_by_user[sub_info.user.id].append(sub_info) stream_ids = {sub_info.stream.id for sub_info in sub_info_list} recent_traffic = get_streams_traffic(stream_ids=stream_ids) # We generally only have a few streams, so we compute stream # data in its own loop. stream_info_dict: Dict[int, StreamInfo] = {} for sub_info in sub_info_list: stream = sub_info.stream if stream.id not in stream_info_dict: email_address = encode_email_address(stream, show_sender=True) stream_weekly_traffic = get_average_weekly_stream_traffic( stream.id, stream.date_created, recent_traffic) if stream.is_in_zephyr_realm and not stream.invite_only: subscribers = [] else: subscribers = list(subscriber_dict[stream.id]) stream_info_dict[stream.id] = StreamInfo( email_address=email_address, stream_weekly_traffic=stream_weekly_traffic, subscribers=subscribers, ) for user_id, sub_infos in info_by_user.items(): sub_dicts: List[APISubscriptionDict] = [] for sub_info in sub_infos: stream = sub_info.stream stream_info = stream_info_dict[stream.id] subscription = sub_info.sub stream_dict = stream.to_dict() # This is verbose as we cannot unpack existing TypedDict # to initialize another TypedDict while making mypy happy. # https://github.com/python/mypy/issues/5382 sub_dict = APISubscriptionDict( # Fields from Subscription.API_FIELDS audible_notifications=subscription.audible_notifications, color=subscription.color, desktop_notifications=subscription.desktop_notifications, email_notifications=subscription.email_notifications, is_muted=subscription.is_muted, pin_to_top=subscription.pin_to_top, push_notifications=subscription.push_notifications, role=subscription.role, wildcard_mentions_notify=subscription.wildcard_mentions_notify, # Computed fields not present in Subscription.API_FIELDS email_address=stream_info.email_address, in_home_view=not subscription.is_muted, stream_weekly_traffic=stream_info.stream_weekly_traffic, subscribers=stream_info.subscribers, # Fields from Stream.API_FIELDS date_created=stream_dict["date_created"], description=stream_dict["description"], first_message_id=stream_dict["first_message_id"], history_public_to_subscribers=stream_dict[ "history_public_to_subscribers"], invite_only=stream_dict["invite_only"], is_web_public=stream_dict["is_web_public"], message_retention_days=stream_dict["message_retention_days"], name=stream_dict["name"], rendered_description=stream_dict["rendered_description"], stream_id=stream_dict["stream_id"], stream_post_policy=stream_dict["stream_post_policy"], # Computed fields not present in Stream.API_FIELDS is_announcement_only=stream_dict["is_announcement_only"], ) sub_dicts.append(sub_dict) # Send a notification to the user who subscribed. event = dict(type="subscription", op="add", subscriptions=sub_dicts) send_event(realm, event, [user_id])