From bd58e3397ffa44a35128cfebdf8511b8db773253 Mon Sep 17 00:00:00 2001 From: akashaviator Date: Tue, 14 Jan 2020 02:41:19 +0530 Subject: [PATCH] events: Extract user_data function from get_raw_user_data. This extracts the user_data inner function from get_raw_user_data as a reusable function. We intend to reuse it for cross-realm user dicts. A few changes were made while extracting it: * Renaming the UserProfile argument to acting_user, so we can do loops over a local user_profile variable. * Moved it to zerver.lib.users, as that's a more appropriate home for this function formatting data on users. * Simplified the calling convention for passing custom profile fields to reflect the fact that this function processes a single user (and is expected to be called in a loop). --- zerver/lib/events.py | 66 ++++++++++++-------------------------------- zerver/lib/users.py | 53 +++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/zerver/lib/events.py b/zerver/lib/events.py index c78e0090a7..f81bf4e73f 100644 --- a/zerver/lib/events.py +++ b/zerver/lib/events.py @@ -14,7 +14,7 @@ from typing import ( session_engine = import_module(settings.SESSION_ENGINE) from zerver.lib.alert_words import user_alert_words -from zerver.lib.avatar import avatar_url, get_avatar_field +from zerver.lib.avatar import avatar_url from zerver.lib.bot_config import load_bot_config_template from zerver.lib.hotspots import get_next_hotspots from zerver.lib.integrations import EMBEDDED_BOTS, WEBHOOK_INTEGRATIONS @@ -37,13 +37,15 @@ from zerver.lib.stream_subscription import handle_stream_notifications_compatibi from zerver.lib.topic import TOPIC_NAME from zerver.lib.topic_mutes import get_topic_mutes from zerver.lib.actions import ( - do_get_streams, get_default_streams_for_realm, + do_get_streams, + get_default_streams_for_realm, gather_subscriptions_helper, get_cross_realm_dicts, get_status_dict, streams_to_dicts_sorted, default_stream_groups_to_dicts_sorted, get_owned_bot_dicts, get_available_notification_sounds, ) +from zerver.lib.users import format_user_row from zerver.lib.user_groups import user_groups_in_realm_serialized from zerver.lib.user_status import get_user_info_dict from zerver.tornado.event_queue import request_event_queue, get_user_events @@ -78,57 +80,23 @@ def get_custom_profile_field_values(realm_id: int) -> Dict[int, Dict[str, Any]]: def get_raw_user_data(realm: Realm, user_profile: UserProfile, client_gravatar: bool, include_custom_profile_fields: bool=True) -> Dict[int, Dict[str, str]]: user_dicts = get_realm_user_dicts(realm.id) + profiles_by_user_id = None + custom_profile_field_data = None if include_custom_profile_fields: profiles_by_user_id = get_custom_profile_field_values(realm.id) + result = {} + for row in user_dicts: + if profiles_by_user_id is not None: + custom_profile_field_data = profiles_by_user_id.get(row['id'], {}) - def user_data(row: Dict[str, Any]) -> Dict[str, Any]: - avatar_url = get_avatar_field( - user_id=row['id'], - realm_id=realm.id, - email=row['delivery_email'], - avatar_source=row['avatar_source'], - avatar_version=row['avatar_version'], - medium=False, - client_gravatar=client_gravatar, - ) - - is_admin = row['role'] == UserProfile.ROLE_REALM_ADMINISTRATOR - is_guest = row['role'] == UserProfile.ROLE_GUEST - is_bot = row['is_bot'] - # This format should align with get_cross_realm_dicts() and notify_created_user - result = dict( - email=row['email'], - user_id=row['id'], - avatar_url=avatar_url, - is_admin=is_admin, - is_guest=is_guest, - is_bot=is_bot, - full_name=row['full_name'], - timezone=row['timezone'], - is_active = row['is_active'], - date_joined = row['date_joined'].isoformat(), - ) - - if (realm.email_address_visibility == Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS and - user_profile.is_realm_admin): - result['delivery_email'] = row['delivery_email'] - - if is_bot: - result["bot_type"] = row["bot_type"] - if row['email'] in settings.CROSS_REALM_BOT_EMAILS: - result['is_cross_realm_bot'] = True - - # Note that bot_owner_id can be None with legacy data. - result['bot_owner_id'] = row['bot_owner_id'] - elif include_custom_profile_fields: - result['profile_data'] = profiles_by_user_id.get(row['id'], {}) - return result - - return { - row['id']: user_data(row) - for row in user_dicts - } + result[row['id']] = format_user_row(realm, + acting_user = user_profile, + row=row, + client_gravatar= client_gravatar, + custom_profile_field_data = custom_profile_field_data + ) + return result def add_realm_logo_fields(state: Dict[str, Any], realm: Realm) -> None: state['realm_logo_url'] = get_realm_logo_url(realm, night = False) diff --git a/zerver/lib/users.py b/zerver/lib/users.py index 311f4d35fb..817a1d0c6f 100644 --- a/zerver/lib/users.py +++ b/zerver/lib/users.py @@ -1,14 +1,15 @@ -from typing import Dict, List, Optional, Tuple, Union, cast +from typing import Any, Dict, List, Optional, Tuple, Union, cast import unicodedata +from django.conf import settings from django.db.models.query import QuerySet from django.utils.translation import ugettext as _ from zerver.lib.cache import generic_bulk_cached_fetch, user_profile_cache_key_id, \ user_profile_by_id_cache_key from zerver.lib.request import JsonableError -from zerver.lib.avatar import avatar_url +from zerver.lib.avatar import avatar_url, get_avatar_field from zerver.lib.exceptions import OrganizationAdministratorRequired from zerver.models import UserProfile, Service, Realm, \ get_user_profile_by_id_in_realm, \ @@ -273,3 +274,51 @@ def compute_show_invites_and_add_streams(user_profile: UserProfile) -> Tuple[boo show_add_streams = False return (show_invites, show_add_streams) + +def format_user_row(realm: Realm, acting_user: UserProfile, row: Dict[str, Any], + client_gravatar: bool, + custom_profile_field_data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: + """Formats a user row returned by a database fetch using + .values(*realm_user_dict_fields) into a dictionary representation + of that user for API delivery to clients. The acting_user + argument is used for permissions checks. + """ + + avatar_url = get_avatar_field(user_id=row['id'], + realm_id=realm.id, + email=row['delivery_email'], + avatar_source=row['avatar_source'], + avatar_version=row['avatar_version'], + medium=False, + client_gravatar=client_gravatar,) + + is_admin = row['role'] == UserProfile.ROLE_REALM_ADMINISTRATOR + is_guest = row['role'] == UserProfile.ROLE_GUEST + is_bot = row['is_bot'] + # This format should align with get_cross_realm_dicts() and notify_created_user + result = dict( + email=row['email'], + user_id=row['id'], + avatar_url=avatar_url, + is_admin=is_admin, + is_guest=is_guest, + is_bot=is_bot, + full_name=row['full_name'], + timezone=row['timezone'], + is_active = row['is_active'], + date_joined = row['date_joined'].isoformat(), + ) + if (realm.email_address_visibility == Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS and + acting_user.is_realm_admin): + result['delivery_email'] = row['delivery_email'] + + if is_bot: + result["bot_type"] = row["bot_type"] + if row['email'] in settings.CROSS_REALM_BOT_EMAILS: + result['is_cross_realm_bot'] = True + + # Note that bot_owner_id can be None with legacy data. + result['bot_owner_id'] = row['bot_owner_id'] + elif custom_profile_field_data is not None: + result['profile_data'] = custom_profile_field_data + return result